/// <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); } }
/// <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); }