/// <summary>
        /// Checks if the VRP request is short enough to be services with the
        /// synchronous VRP service.
        /// </summary>
        /// <param name="request">The reference to the request object to be
        /// analyzed.</param>
        /// <returns>True if the request could be executed with a synchronous VRP
        /// service.</returns>
        public bool CanExecuteSyncronously(SubmitVrpJobRequest request)
        {
            Debug.Assert(request != null);
            Debug.Assert(request.Orders != null);
            Debug.Assert(request.Orders.Features != null);
            Debug.Assert(request.Routes != null);
            Debug.Assert(request.Routes.Features != null);

            if (request.Orders.Features.Length > _maxOrders ||
                request.Routes.Features.Length > _maxRoutes)
            {
                return false;
            }

            return true;
        }
        ///////////////////////////////////////////////////////////////////////////////////////////
        ///////////////////////////////////////////////////////////////////////////////////////////
        public IList<RouteResult> Convert(GPRouteResult gpResult,
            BatchRouteSolveResponse routeResponse,
            SubmitVrpJobRequest request)
        {
            Debug.Assert(gpResult != null);
            Debug.Assert(request != null);

            var directionsFeatures = default(ILookup<Guid, GPFeature>);
            if (gpResult.Directions != null)
            {
                directionsFeatures = gpResult.Directions.Features.ToLookup(feature =>
                    _AttrToObjectId(NAAttribute.ROUTE_NAME, feature.Attributes));
            }

            var hasDirections = routeResponse != null || directionsFeatures != null;

            // route results
            var results = new List<RouteResult>();
            foreach (GPFeature feature in gpResult.Routes.Features)
            {
                // check violation status
                if (!_IsObjectViolated(feature))
                {
                    // route id
                    Guid routeId = _AttrToObjectId(NAAttribute.NAME, feature.Attributes);

                    // find route
                    Route route = DataObjectHelper.FindObjectById<Route>(routeId, _schedule.Routes);

                    if (route == null)
                    {
                        string message = Properties.Messages.Error_InvalidGPFeatureMapping;
                        throw new RouteException(message); // exception
                    }

                    List<StopData> stops = _GetRouteStops(gpResult, route, request);
                    // Get directions for route
                    if (stops.Count != 0 && hasDirections)
                    {
                        var directions = default(IEnumerable<DirectionEx>);

                        if (routeResponse != null)
                        {
                            // Use Routing service directions.
                            var routeDirs = _FindRouteDirections(routeResponse, routeId);
                            if (routeDirs == null || !_ContainsDirFeatures(routeDirs))
                            {   // route has stops, so there must be directions
                                throw _CreateNoDirectionsError(routeId);
                            }

                            directions = routeDirs.Features
                                .Select(DirectionsHelper.ConvertToDirection)
                                .ToList();
                        }
                        else if (directionsFeatures != null)
                        {
                            // Use VRP service directions
                            var directionFeatures = directionsFeatures[routeId];

                            // WORKAROUND: pass first stop geometry as geometry whihc will be used
                            // in case if directions has no geometry.
                            _FixGeometries(directionFeatures, routeId, stops.First().Geometry as GPPoint);

                            directions = directionFeatures
                                .Select(DirectionsHelper.ConvertToDirection)
                                .ToList();
                        }

                        // We should have either Routing or VRP service directions here.
                        Debug.Assert(directions != null);
                        DirectionsHelper.SetDirections(stops, directions);
                    }

                    // set order sequence values
                    _SetOrderSequence(stops);

                    // add route result
                    results.Add(_CreateRouteResult(feature, route, stops));
                }
            }

            return results;
        }
        ///////////////////////////////////////////////////////////////////////////////////////////
        ///////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// Method gets stops collection from features collection.
        /// </summary>
        /// <param name="gpResult">GP Route result from server.</param>
        /// <param name="route">Route to get route info.</param>
        /// <param name="request">Vrp request.</param>
        /// <returns></returns>
        private List<StopData> _GetRouteStops(
            GPRouteResult gpResult,
            Route route,
            SubmitVrpJobRequest request)
        {
            Debug.Assert(gpResult != null);
            Debug.Assert(route != null);

            var stopsByType = gpResult.Stops.Features
                .ToLookup(feature => feature.Attributes.Get<NAStopType>(NAAttribute.StopType));

            var stops = new List<StopData>();

            // process orders
            stops.AddRange(_GetOrderStops(stopsByType[NAStopType.Order], route));

            // process breaks
            stops.AddRange(_GetBreakStops(stopsByType[NAStopType.Break], route));

            var renewals = request.Renewals == null ?
                Enumerable.Empty<GPFeature>() : request.Renewals.Features;

            // process depots
            stops.AddRange(_GetDepotStops(
                stopsByType[NAStopType.Depot],
                route,
                renewals));

            SolveHelper.SortBySequence(stops);
            if (!stops.Any())
            {
                return stops;
            }

            var isDepot = Functional.MakeLambda(
                (StopData stop) => stop.StopType == StopType.Location);

            var startingDepot = stops.FirstOrDefault(isDepot);
            if (startingDepot != null)
            {
                startingDepot.TimeAtStop = route.TimeAtStart;
            }

            var endingDepot = stops.LastOrDefault(isDepot);
            if (endingDepot != null)
            {
                endingDepot.TimeAtStop = route.TimeAtEnd;
            }

            return stops;
        }
        protected override IList<RouteResult> ConvertResult(
            VrpResult vrpResult,
            SubmitVrpJobRequest request)
        {
            // convert current route results
            List<RouteResult> routeResults = new List<RouteResult>(
                base.ConvertResult(vrpResult, request));

            // add route results from previous solve for those routes that
            // were not used in the last solve
            List<RouteResult> prevRouteResults = new List<RouteResult>();
            foreach (RouteResult rr in _prevRouteResults)
            {
                if (!_ContainsRoute(routeResults, rr.Route))
                    prevRouteResults.Add(rr);
            }

            routeResults.AddRange(prevRouteResults);

            return routeResults;
        }
        ///////////////////////////////////////////////////////////////////////////////////////////
        ///////////////////////////////////////////////////////////////////////////////////////////
        /// <summary>
        /// Method collect information from solve request data for planned date and build request
        /// object.
        /// </summary>
        /// <param name="schedule">Current schedule.</param>
        /// <param name="reqData">Request data to get information.</param>
        /// <param name="options">Request options.</param>
        /// <param name="solveOptions">The reference to to the solve options object.</param>
        /// <returns>Request object with filled information for request.</returns>
        /// <exception cref="RouteException">If Fuel Economy in some of routes is 0.0,
        /// If unassigned order or location is not geocoded.</exception>
        public SubmitVrpJobRequest BuildRequest(Schedule schedule, SolveRequestData reqData,
            SolveRequestOptions options, SolveOptions solveOptions)
        {
            Debug.Assert(schedule != null);
            Debug.Assert(reqData != null);
            Debug.Assert(options != null);
            Debug.Assert(solveOptions != null);

            _plannedDate = (DateTime)schedule.PlannedDate;
            _orderRevenue = Math.Min(_CalcMaxRoutesCost(reqData.Routes),
                MAX_REVENUE);

            _BuildDepotsColl(reqData.Routes);

            SubmitVrpJobRequest req = new SubmitVrpJobRequest();

            // Get depots.
            req.Depots = _ConvertDepots();

            // Get orders.
            req.Orders = _ConvertOrders(reqData.Orders, reqData.Routes,
                options.ConvertUnassignedOrders);

            // Get order pairs.
            req.OrderPairs = _ConvertOrderPairs(reqData.Orders);

            // Get routes.
            req.Routes = _ConvertRoutes(reqData.Routes);

            // Get route zones.
            var zoneInfo = _ConvertZones(reqData.Routes);
            req.RouteZones = zoneInfo.Zones;
            req.SpatiallyClusterRoutes = zoneInfo.UseSpatialClustering;

            // Get renewals.
            req.Renewals = _ConvertRenewals(reqData.Routes);

            // Get breaks.
            req.Breaks = ConvertBreaks(reqData.Routes);

            // Get barriers of all types.
            var typedBarriers = reqData.Barriers.ToLookup(BarriersConverter.DetermineBarrierType);
            req.PointBarriers = _ConvertBarriers(typedBarriers, BarrierGeometryType.Point,
                _context.NetworkDescription.NetworkAttributes);
            req.PolygonBarriers = _ConvertBarriers(typedBarriers, BarrierGeometryType.Polygon,
                _context.NetworkDescription.NetworkAttributes);
            req.LineBarriers = _ConvertBarriers(typedBarriers, BarrierGeometryType.Polyline,
                _context.NetworkDescription.NetworkAttributes);

            // Get network attribute parameters.
            req.NetworkParams = _ConvertNetworkParams();

            // Get analysis region.
            req.AnalysisRegion = _context.RegionName;

            // Get restrictions.
            req.Restrictions = _FormatRestrictions();

            // Get u-turn policy.
            req.UTurnPolicy = _GetUTurnPolicy();

            req.Date = GPObjectHelper.DateTimeToGPDateTime(_plannedDate);
            req.UseHierarchyInAnalysis = true;
            req.PopulateRouteLines = false;
            req.EnvOutSR = solverSR.WKID;
            req.EnvProcessSR = solverSR.WKID;
            req.TWPreference = _context.SolverSettings.TWPreference;
            req.ExcludeRestrictedStreets = _context.SolverSettings.ExcludeRestrictedStreets;
            req.OutputFormat = NAOutputFormat.JSON;

            req.DirectionsLanguage = RequestBuildingHelper.GetDirectionsLanguage();
            req.PopulateDirections = options.PopulateRouteLines;
            req.SaveOutputLayer = _context.SolverSettings.SaveOutputLayer;

            req.ReturnM = true;

            return req;
        }
 /// <summary>
 /// Does nothing but returns boolean constant passed to the constructor.
 /// </summary>
 /// <param name="request">The reference to the request object to be
 /// analyzed.</param>
 /// <returns>A value passed to the constructor.</returns>
 public bool CanExecuteSyncronously(SubmitVrpJobRequest request)
 {
     return _executeSynchronously;
 }