/// <summary>
        /// Attempts to geocode a list of simple waypoints.
        /// </summary>
        /// <param name="waypoints">A list of simple waypoints to geocode.</param>
        /// <param name="baseRequest">A base request that has the information need to perform a geocode, primarily a Bing Maps key.</param>
        /// <returns>A Task in which a list of simple waypoints will be geocoded.</returns>
        public static async Task TryGeocodeWaypoints(List <SimpleWaypoint> waypoints, BaseRestRequest baseRequest)
        {
            var geocodeTasks = new List <Task>();

            foreach (var wp in waypoints)
            {
                if (wp != null && wp.Coordinate == null && !string.IsNullOrEmpty(wp.Address))
                {
                    geocodeTasks.Add(TryGeocode(wp, baseRequest));
                }
            }

            if (geocodeTasks.Count > 0)
            {
                await ServiceHelper.WhenAllTaskLimiter(geocodeTasks).ConfigureAwait(false);
            }
        }
示例#2
0
        /// <summary>
        /// Executes the request. If there are more waypoints than the batchSize value (default 25), only a MaxSolutions is set to 1, and Tolerances is set to null.
        /// </summary>
        /// <param name="remainingTimeCallback">A callback function in which the estimated remaining time in seconds is sent.</param>
        /// <returns>A response containing the requested data.</returns>
        public override async Task <Response> Execute(Action <int> remainingTimeCallback)
        {
            Response response = null;

            if (WaypointOptimization != null && WaypointOptimization.HasValue && Waypoints.Count >= 2)
            {
                var wpHash = ServiceHelper.GetSequenceHashCode <SimpleWaypoint>(Waypoints);

                var      optionHash = Enum.GetName(typeof(TspOptimizationType), WaypointOptimization);
                var      mode       = TravelModeType.Driving;
                DateTime?depart     = null;

                if (RouteOptions != null)
                {
                    optionHash += "|" + Enum.GetName(typeof(RouteTimeType), RouteOptions.TimeType);

                    if (RouteOptions.TimeType == RouteTimeType.Departure && RouteOptions.DateTime != null && RouteOptions.DateTime.HasValue)
                    {
                        depart      = RouteOptions.DateTime;
                        optionHash += "|" + RouteOptions.DateTime.ToString();
                    }

                    mode = RouteOptions.TravelMode;

                    optionHash += "|" + Enum.GetName(typeof(TravelModeType), mode);
                }
                else
                {
                    optionHash += "|" + Enum.GetName(typeof(RouteTimeType), RouteTimeType.Departure);
                }

                //Check to see if the waypoints have changed since they were last optimized.
                if (waypointsHash != wpHash || string.Compare(optimizationOptionHash, optionHash) != 0)
                {
                    var tspResult = await TravellingSalesmen.Solve(Waypoints, mode, WaypointOptimization, depart, BingMapsKey);

                    Waypoints = tspResult.OptimizedWaypoints;

                    //Update the stored hashes to prevent unneeded optimizations in the future if not needed.
                    waypointsHash          = ServiceHelper.GetSequenceHashCode <SimpleWaypoint>(Waypoints);
                    optimizationOptionHash = optionHash;
                }
            }

            var requestUrl = GetRequestUrl();

            int startIdx = 0;
            int endIdx   = 0;

            if (Waypoints.Count <= batchSize)
            {
                if (RouteOptions != null && RouteOptions.TravelMode == TravelModeType.Truck)
                {
                    var requestBody = GetTruckPostRequestBody(startIdx, out endIdx);

                    response = await ServiceHelper.MakeAsyncPostRequest(requestUrl, requestBody, remainingTimeCallback);
                }
                else
                {
                    remainingTimeCallback?.Invoke(1);

                    using (var responseStream = await ServiceHelper.GetStreamAsync(new Uri(requestUrl)))
                    {
                        response = ServiceHelper.DeserializeStream <Response>(responseStream);
                    }
                }
            }
            else
            {
                //There is more waypoints than the batchSize value (default 25), break it up into multiple requests. Only allow a single route in the response and no tolerances.
                if (RouteOptions != null)
                {
                    if (RouteOptions.MaxSolutions > 1)
                    {
                        RouteOptions.MaxSolutions = 1;
                    }

                    RouteOptions.Tolerances = null;
                }

                if (Waypoints == null)
                {
                    throw new Exception("Waypoints not specified.");
                }
                else if (Waypoints.Count < 2)
                {
                    throw new Exception("Not enough Waypoints specified.");
                }
                else if (Waypoints[0].IsViaPoint || Waypoints[Waypoints.Count - 1].IsViaPoint)
                {
                    throw new Exception("Start and end waypoints must not be ViaWaypoints.");
                }

                var requestUrls   = new List <string>();
                var requestBodies = new List <string>();

                while (endIdx < Waypoints.Count - 1)
                {
                    if (RouteOptions != null && RouteOptions.TravelMode == TravelModeType.Truck)
                    {
                        requestUrls.Add(requestUrl);
                        requestBodies.Add(GetTruckPostRequestBody(startIdx, out endIdx));
                    }
                    else
                    {
                        requestUrls.Add(GetRequestUrl(startIdx, out endIdx));
                        requestBodies.Add(null);
                    }

                    startIdx = endIdx - 1;
                }

                if (remainingTimeCallback != null)
                {
                    int batchProcessingTime = (int)Math.Ceiling((double)requestUrls.Count / (double)ServiceManager.QpsLimit);

                    if (RouteOptions != null && RouteOptions.TravelMode == TravelModeType.Truck)
                    {
                        //Use an average of 4 seconds per batch for processing truck routes as multiplier for the processing time.
                        //Other routes typically take less than a second and as such 1 second is used for those but isn't needed as a multiplier.
                        batchProcessingTime *= 4;
                    }

                    remainingTimeCallback(batchProcessingTime);
                }

                var routes     = new Route[requestUrls.Count];
                var routeTasks = new List <Task>();

                for (var i = 0; i < routes.Length; i++)
                {
                    routeTasks.Add(CalculateRoute(requestUrls[i], requestBodies[i], i, routes));
                }

                if (routeTasks.Count > 0)
                {
                    await ServiceHelper.WhenAllTaskLimiter(routeTasks);
                }

                try
                {
                    response = new Response()
                    {
                        StatusCode        = 200,
                        StatusDescription = "OK",
                        ResourceSets      = new ResourceSet[]
                        {
                            new ResourceSet()
                            {
                                Resources = new Resource[]
                                {
                                    await MergeRoutes(routes)
                                }
                            }
                        }
                    };
                }
                catch (Exception ex)
                {
                    return(new Response()
                    {
                        StatusCode = 500,
                        StatusDescription = "Error",
                        ErrorDetails = new string[]
                        {
                            ex.Message
                        }
                    });
                }
            }

            return(response);
        }