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