/// <summary> /// Method determines is GP feature object violated. /// </summary> /// <param name="feature">GP feature.</param> /// <returns>True if object violated, otherwise - false.</returns> private static bool _IsObjectViolated(GPFeature feature) { int vc; return _GetViolatedConstraint(feature, out vc); }
/// <summary> /// Create line which contain of two points with equal coordinates and set it as /// items geometry. /// </summary> /// <param name="item">GPFeature wich geometry must be set.</param> /// <param name="point">Point which will be used for polyline.</param> private static void _FillGeometry(GPFeature item, double[] point) { item.Geometry = new GeometryHolder { Value = new GPPolyline { Paths = new[] { new[] { point, point } }, }, }; }
/// <summary> /// Method gets violated constraint from GP feature attributes. /// </summary> /// <param name="feature">GP feature.</param> /// <param name="constraint">Violated constraint as output parameter.</param> /// <returns>True if any violated constraint was found, otherwise - false.</returns> private static bool _GetViolatedConstraint(GPFeature feature, out int constraint) { return feature.Attributes.TryGet<int>(NAAttribute.VIOLATED_CONSTRAINTS, out constraint); }
/// <summary> /// Method creates stop from depot. /// </summary> /// <param name="depotVisitFeature">Depot GP feature.</param> /// <param name="route">Route.</param> /// <param name="renewalsByID">Renewals collection associated with its Id.</param> /// <returns>Stop data.</returns> private StopData _CreateStopFromDepot( GPFeature depotVisitFeature, Route route, IDictionary<Guid, GPFeature> renewalsByID) { Debug.Assert(depotVisitFeature != null); Debug.Assert(route != null); Debug.Assert(renewalsByID != null); StopData stop = new StopData(); // Distance. stop.Distance = depotVisitFeature.Attributes.Get<double>(NAAttribute.FROM_PREV_DISTANCE); // Sequence number. stop.SequenceNumber = depotVisitFeature.Attributes.Get<int>(NAAttribute.SEQUENCE); // Wait time. stop.WaitTime = depotVisitFeature.Attributes.Get<double>(NAAttribute.WAIT_TIME, 0.0); // Travel time. stop.TravelTime = depotVisitFeature.Attributes.Get<double>(NAAttribute.FROM_PREV_TRAVEL_TIME, 0.0); // Arrive time. stop.ArriveTime = _Get(depotVisitFeature.Attributes, NAAttribute.ARRIVE_TIME); // Associated object. Guid assocObjectId = _AttrToObjectId(NAAttribute.NAME, depotVisitFeature.Attributes); Location loc = _GetRouteLocation(route, assocObjectId); if (loc == null) { string message = Properties.Messages.Error_InvalidGPFeatureMapping; throw new RouteException(message); // exception } stop.AssociatedObject = loc; var renewalFeature = default(GPFeature); if (renewalsByID.TryGetValue(assocObjectId, out renewalFeature)) { stop.TimeAtStop = renewalFeature.Attributes.Get<double>( NAAttribute.SERVICE_TIME, 0.0); } var plannedDate = route.Schedule.PlannedDate.Value; var timeWindow = loc.TimeWindow.ToDateTime(plannedDate); stop.TimeWindowStart1 = timeWindow.Item1; stop.TimeWindowEnd1 = timeWindow.Item2; // Curb approach. stop.NACurbApproach = CurbApproachConverter.ToNACurbApproach( _settings.GetDepotCurbApproach()); // Route id. stop.RouteId = route.Id; // Geometry. if (loc.GeoLocation != null) { stop.Geometry = GPObjectHelper.PointToGPPoint(loc.GeoLocation.Value); } // Stop type. stop.StopType = StopType.Location; return stop; }
/// <summary> /// Method creates stop from order GP feature. /// </summary> /// <param name="feature">Order GP feature</param> /// <param name="route">Route.</param> /// <returns>Stop data.</returns> private StopData _CreateStopFromOrder(GPFeature feature, Route route) { var stop = new StopData(); // distance stop.Distance = feature.Attributes.Get<double>(NAAttribute.FROM_PREV_DISTANCE); // sequence number stop.SequenceNumber = feature.Attributes.Get<int>(NAAttribute.SEQUENCE); // wait time stop.WaitTime = feature.Attributes.Get<double>(NAAttribute.WAIT_TIME, 0.0); // service time stop.TimeAtStop = feature.Attributes.Get<double>(NAAttribute.SERVICE_TIME, 0.0); // travel time stop.TravelTime = feature.Attributes.Get<double>(NAAttribute.FROM_PREV_TRAVEL_TIME, 0.0); // arrive time var arriveTime = _Get(feature.Attributes, NAAttribute.ARRIVE_TIME); // set stop arrive time = real arrive time + wait time stop.ArriveTime = arriveTime.AddMinutes(stop.WaitTime); // TW1 stop.TimeWindowStart1 = _TryGet(feature.Attributes, NAAttribute.TW_START1); stop.TimeWindowEnd1 = _TryGet(feature.Attributes, NAAttribute.TW_END1); // TW2 stop.TimeWindowStart2 = _TryGet(feature.Attributes, NAAttribute.TW_START2); stop.TimeWindowEnd2 = _TryGet(feature.Attributes, NAAttribute.TW_END2); // curbapproach stop.NACurbApproach = (NACurbApproachType)feature.Attributes.Get<int>(NAAttribute.ArriveCurbApproach); // associated object Guid assocObjectId = _AttrToObjectId(NAAttribute.NAME, feature.Attributes); Order order = _project.Orders.SearchById(assocObjectId); if (order == null) { string message = Properties.Messages.Error_InvalidGPFeatureMapping; throw new RouteException(message); // exception } stop.AssociatedObject = order; stop.TimeAtStop = order.ServiceTime; var plannedDate = route.Schedule.PlannedDate.Value; var timeWindow = order.TimeWindow.ToDateTime(plannedDate); stop.TimeWindowStart1 = timeWindow.Item1; stop.TimeWindowEnd1 = timeWindow.Item2; timeWindow = order.TimeWindow2.ToDateTime(plannedDate); stop.TimeWindowStart2 = timeWindow.Item1; stop.TimeWindowEnd2 = timeWindow.Item2; // route id stop.RouteId = _AttrToObjectId(NAAttribute.ROUTE_NAME, feature.Attributes); // geometry if (order.GeoLocation != null) { stop.Geometry = GPObjectHelper.PointToGPPoint(order.GeoLocation.Value); } // stop type stop.StopType = StopType.Order; return stop; }
/// <summary> /// Method route result. /// </summary> /// <param name="feature">GP feature.</param> /// <param name="route">Route.</param> /// <param name="stops">Stop data collection.</param> /// <returns>Route result.</returns> private RouteResult _CreateRouteResult(GPFeature feature, Route route, IList<StopData> stops) { RouteResult result = new RouteResult(); // cost result.Cost = feature.Attributes.Get<double>(NAAttribute.TOTAL_COST, 0.0); // start time result.StartTime = _TryGet(feature.Attributes, NAAttribute.START_TIME); // end time result.EndTime = _TryGet(feature.Attributes, NAAttribute.END_TIME); // total time result.TotalTime = feature.Attributes.Get<double>(NAAttribute.TOTAL_TIME, 0.0); // total distance result.TotalDistance = feature.Attributes.Get<double>(NAAttribute.TOTAL_DISTANCE, 0.0); // total travel time result.TravelTime = feature.Attributes.Get<double>(NAAttribute.TOTAL_TRAVEL_TIME, 0.0); // total violation time result.ViolationTime = feature.Attributes.Get<double>(NAAttribute.TOTAL_VIOLATION_TIME, 0.0); // total wait time result.WaitTime = feature.Attributes.Get<double>(NAAttribute.TOTAL_WAIT_TIME, 0.0); // overtime double overtime = result.TotalTime - route.Driver.TimeBeforeOT; if (overtime > 0) result.Overtime = overtime; // capacities result.Capacities = _CreateCapacities(stops); // path // currently we use directions to get route geometry // route result.Route = route; // set stops var resStops = new List<StopData>(); // When a break(s) has preassigned sequence, it could present alone in output // stops collection, so in this case there will be no stops at all. // Checks in stop sequence present at least one order if (stops.Any(stop => stop.StopType == StopType.Order)) resStops.AddRange(stops); result.Stops = resStops; return result; }
/// <summary> /// Method creates stop from break GP feature. /// </summary> /// <param name="feature">Break GP feature</param> /// <param name="route">Route.</param> /// <param name="routeBreak">Break from route.</param> /// <param name="stop">Stop data to fill.</param> /// <returns>True - if successfully created, otherwise - False.</returns> private bool _CreateStopFromBreak(GPFeature feature, Route route, Break routeBreak, out StopData stop) { Debug.Assert(feature != null); Debug.Assert(route != null); Debug.Assert(routeBreak != null); // Check sequence number. int sequenceNumber = 0; if (!feature.Attributes.TryGet<int>(NAAttribute.SEQUENCE, out sequenceNumber) // Sequence always starts from 1. Otherwise: this is empty break. || sequenceNumber == 0) { stop = null; return false; } stop = new StopData(); stop.SequenceNumber = sequenceNumber; // Distance. stop.Distance = feature.Attributes.Get<double>(NAAttribute.FROM_PREV_DISTANCE); // Wait time. stop.WaitTime = feature.Attributes.Get<double>(NAAttribute.WAIT_TIME, 0.0); // Service time. stop.TimeAtStop = routeBreak.Duration; // Travel time. stop.TravelTime = feature.Attributes.Get<double>(NAAttribute.FROM_PREV_TRAVEL_TIME, 0.0); // Time Windows for break stop. var breakTimeWindow = _GetBreakTimeWindow(routeBreak); var plannedDate = route.Schedule.PlannedDate.Value; var timeWindow = breakTimeWindow.ToDateTime(plannedDate); stop.TimeWindowStart1 = timeWindow.Item1; stop.TimeWindowEnd1 = timeWindow.Item2; // Arrive time. var arriveTime = _TryGet(feature.Attributes, NAAttribute.ARRIVE_TIME); if (!arriveTime.HasValue) { arriveTime = stop.TimeWindowStart1; } arriveTime = arriveTime.AddMinutes(stop.WaitTime); stop.ArriveTime = arriveTime.Value; // Route id. stop.RouteId = _AttrToObjectId(NAAttribute.ROUTE_NAME, feature.Attributes); // Geometry: // There is no geometry for breaks. // Curb approach. stop.NACurbApproach = NACurbApproachType.esriNAEitherSideOfVehicle; // Stop type. stop.StopType = StopType.Lunch; // Break stop does not have an associated object. stop.AssociatedObject = null; return true; }
/// <summary> /// Method converts network parameters.into GPRecordSet. /// </summary> /// <returns>Network parameters GPRecordSet.</returns> private GPRecordSet _ConvertNetworkParams() { List<GPFeature> features = new List<GPFeature>(); foreach (NetworkAttribute attr in _context.NetworkDescription.NetworkAttributes) { foreach (NetworkAttributeParameter param in attr.Parameters) { object value = null; if (_context.SolverSettings.GetNetworkAttributeParameterValue( attr.Name, param.Name, out value)) { // Skip null value overrides, let the service to use defaults. if (value != null) { GPFeature feature = new GPFeature(); feature.Attributes = new AttrDictionary(); feature.Attributes.Add(NAAttribute.NETWORK_ATTR_NAME, attr.Name); feature.Attributes.Add(NAAttribute.NETWORK_ATTR_PARAM_NAME, param.Name); feature.Attributes.Add(NAAttribute.NETWORK_ATTR_PARAM_VALUE, value); features.Add(feature); } } } } GPRecordSet rs = null; if (features.Count > 0) { rs = new GPRecordSet(); rs.Features = features.ToArray(); } return rs; }
/// <summary> /// Method converts order into GPFeature. /// </summary> /// <param name="unassignedOrder">Unassigned order to convert.</param> /// <param name="assignedOrder">Assigned order to convert.</param> /// <returns>Orders GPFeature.</returns> /// <exception cref="RouteException">If unassigned order is not geocoded.</exception> private GPFeature _ConvertOrder(Order unassignedOrder, AssignedOrder assignedOrder) { Debug.Assert(unassignedOrder != null); GPFeature feature = new GPFeature(); // Geometry. IGeocodable gc = unassignedOrder as IGeocodable; Debug.Assert(gc != null); if (!gc.IsGeocoded) { throw new RouteException(String.Format( Properties.Messages.Error_OrderIsUngeocoded, unassignedOrder.Id)); } Debug.Assert(gc.GeoLocation != null); feature.Geometry = new GeometryHolder(); feature.Geometry.Value = GPObjectHelper.PointToGPPoint((Point)gc.GeoLocation); // Attributes. AttrDictionary attrs = new AttrDictionary(); // Name. attrs.Add(NAAttribute.NAME, unassignedOrder.Id.ToString()); // Curb approach. attrs.Add(NAAttribute.CURB_APPROACH, (int)CurbApproachConverter.ToNACurbApproach( _context.SolverSettings.GetOrderCurbApproach())); // Service time. attrs.Add(NAAttribute.SERVICE_TIME, unassignedOrder.ServiceTime); // Time windows. TimeWindow timeWindow1 = unassignedOrder.TimeWindow; TimeWindow timeWindow2 = unassignedOrder.TimeWindow2; _SetTimeWindowsAttributes(attrs, ref timeWindow1, ref timeWindow2); // Max Violation Time 1. attrs.Add(NAAttribute.MAX_VIOLATION_TIME1, timeWindow1.IsWideOpen ? (double?)null : unassignedOrder.MaxViolationTime); // Max Violation Time 2. attrs.Add(NAAttribute.MAX_VIOLATION_TIME2, timeWindow2.IsWideOpen ? (double?)null : unassignedOrder.MaxViolationTime); // PickUp or DropOff quantities. string capacities = _FormatCapacities(unassignedOrder.Capacities); if (unassignedOrder.Type == OrderType.Delivery) { attrs.Add(NAAttribute.DELIVERY, capacities); attrs.Add(NAAttribute.PICKUP, null); } else { attrs.Add(NAAttribute.PICKUP, capacities); attrs.Add(NAAttribute.DELIVERY, null); } // Revenue. attrs.Add(NAAttribute.REVENUE, unassignedOrder.Priority == OrderPriority.Normal ? 0 : (long)_orderRevenue); // Specialties. List<Guid> specIds = GetOrderSpecIds(unassignedOrder); if (specIds.Count > 0) attrs.Add(NAAttribute.SPECIALTY_NAMES, _FormatSpecList(specIds)); if (assignedOrder != null) SetOrderAssignment(attrs, assignedOrder); else SetOrderAssignment(attrs, unassignedOrder); // Status. attrs.Add(NAAttribute.STATUS, (int)NAObjectStatus.esriNAObjectStatusOK); feature.Attributes = attrs; return feature; }
/// <summary> /// Method creates GPFeature for seed point. /// </summary> /// <param name="route">Route to get information.</param> /// <param name="pt">Point location of seed point.</param> /// <returns>Seed point GPFeature.</returns> private static GPFeature _CreateSeedPointFeature(Route route, Point? pt) { Debug.Assert(route != null); Debug.Assert(pt != null); GPFeature feature = new GPFeature(); feature.Attributes = new AttrDictionary(); feature.Attributes.Add(NAAttribute.ROUTE_NAME, route.Id.ToString()); feature.Attributes.Add(NAAttribute.SEED_POINT_TYPE, (int)NARouteSeedPointType.esriNARouteSeedPointStatic); Point seedPoint = (Point)pt; feature.Geometry = new GeometryHolder(); feature.Geometry.Value = GPObjectHelper.PointToGPPoint(seedPoint); return feature; }
/// <summary> /// Method converts location into GPFeature. /// </summary> /// <param name="loc">Location to convert.</param> /// <returns>Locations GPFeature.</returns> /// <exception cref="RouteException">If location is not geocoded.</exception> private GPFeature _ConvertDepot(Location loc) { Debug.Assert(loc != null); GPFeature feature = new GPFeature(); // Geometry. feature.Geometry = new GeometryHolder(); feature.Geometry.Value = GPObjectHelper.PointToGPPoint(_GetLocationPoint(loc)); // Attributes. AttrDictionary attrs = new AttrDictionary(); // Name. attrs.Add(NAAttribute.NAME, loc.Id.ToString()); // Curb approach. attrs.Add(NAAttribute.CURB_APPROACH, (int)CurbApproachConverter.ToNACurbApproach( _context.SolverSettings.GetDepotCurbApproach())); // Set Time Windows attributes. TimeWindow timeWindow1 = loc.TimeWindow; TimeWindow timeWindow2 = loc.TimeWindow2; _SetTimeWindowsAttributes(attrs, ref timeWindow1, ref timeWindow2); feature.Attributes = attrs; return feature; }
/// <summary> /// Method converts zones and seed points from route to GPFeatures and fill collections /// in parameters. /// </summary> /// <param name="route">Route.</param> /// <param name="type">Zone type.</param> /// <param name="zoneFeatures">Zones GPFeatures collection to fill.</param> /// <param name="seedPointFeatures">Seed points GPFeatures collection to fill.</param> private void _ConvertRouteZones(Route route, RouteZoneType type, List<GPFeature> zoneFeatures, List<GPFeature> seedPointFeatures) { Debug.Assert(type != RouteZoneType.None); if (_HasValidZones(route)) { if (type == RouteZoneType.Point) { seedPointFeatures.Add(_CreateSeedPointFeature(route, _GetZonesCentroid(route.Zones))); } else if (type == RouteZoneType.Polygon) { GPFeature feature = new GPFeature(); feature.Attributes = new AttrDictionary(); feature.Attributes.Add(NAAttribute.ROUTE_NAME, route.Id.ToString()); feature.Attributes.Add(NAAttribute.IS_HARD_ZONE, route.HardZones); // Geometry. feature.Geometry = new GeometryHolder(); feature.Geometry.Value = GPObjectHelper.PolygonToGPPolygon( _GetZonesPolygon(route.Zones)); zoneFeatures.Add(feature); } else Debug.Assert(false); } else { // Zones count = 0 and some another routes contain point then // just set midpoint between the start and end location. if (type == RouteZoneType.Point) { seedPointFeatures.Add(_CreateSeedPointFeature(route, _GetRouteLocationsMidpoint(route))); } } }
/// <summary> /// Method converts route into GPFeature. /// </summary> /// <param name="route">Route.</param> /// <returns>Routes GPFeature.</returns> /// <exception cref="RouteException">If Fuel Economy in routes is 0.0.</exception> private GPFeature _ConvertRoute(Route route) { Debug.Assert(route != null); // Attributes. AttrDictionary attrs = new AttrDictionary(); // Name. attrs.Add(NAAttribute.NAME, route.Id.ToString()); // Start depot. attrs.Add(NAAttribute.START_DEPOT_NAME, route.StartLocation == null ? null : route.StartLocation.Id.ToString()); // End depot. attrs.Add(NAAttribute.END_DEPOT_NAME, route.EndLocation == null ? null : route.EndLocation.Id.ToString()); // Depot service times. attrs.Add(NAAttribute.START_DEPOT_SERVICE_TIME, route.StartLocation == null ? null : (object)route.TimeAtStart); attrs.Add(NAAttribute.END_DEPOT_SERVICE_TIME, route.EndLocation == null ? null : (object)route.TimeAtEnd); // Grace period. _SetTimeWindowAttribute(route.StartTimeWindow, NAAttribute.EARLIEST_START_TIME, NAAttribute.LATEST_START_TIME, attrs); // Capacities. attrs.Add(NAAttribute.CAPACITIES, _FormatCapacities(route.Vehicle.Capacities)); // Costs. attrs.Add(NAAttribute.FIXED_COST, route.Driver.FixedCost + route.Vehicle.FixedCost); attrs.Add(NAAttribute.COST_PER_UNIT_TIME, route.Driver.PerHourSalary / 60); attrs.Add(NAAttribute.COST_PER_UNIT_OVERTIME, route.Driver.PerHourOTSalary / 60); attrs.Add(NAAttribute.OVERTIME_START_TIME, route.Driver.TimeBeforeOT); if (route.Vehicle.FuelEconomy == 0.0) throw new RouteException(Properties.Messages.Error_InvalidFuelConsumptionValue); attrs.Add(NAAttribute.COST_PER_UNIT_DISTANCE, route.Vehicle.FuelType.Price / route.Vehicle.FuelEconomy); // Order constraints. attrs.Add(NAAttribute.MAX_ORDERS, route.MaxOrders); // Max drive duration. attrs.Add(NAAttribute.MAX_TOTAL_TRAVEL_TIME, route.MaxTravelDuration == 0.0 ? null : (object)route.MaxTravelDuration); // Max total duration. attrs.Add(NAAttribute.MAX_TOTAL_TIME, route.MaxTotalDuration == 0.0 ? null : (object)route.MaxTotalDuration); // Max drive distance. attrs.Add(NAAttribute.MAX_TOTAL_DISTANCE, route.MaxTravelDistance == 0.0 ? null : (object)route.MaxTravelDistance); // Arrive and depart delay. attrs.Add(NAAttribute.ARRIVE_DEPART_DELAY, _context.SolverSettings.ArriveDepartDelay); // Specialties. List<Guid> specIds = GetRouteSpecIds(route); if (specIds.Count > 0) attrs.Add(NAAttribute.SPECIALTY_NAMES, _FormatSpecList(specIds)); // assignment rule. attrs.Add(NAAttribute.ASSIGNMENT_RULE, (int)NARouteAssignmentRule.esriNARouteIncludeInSolve); // TODO: dynamic point zones. GPFeature feature = new GPFeature(); feature.Attributes = attrs; return feature; }
/// <summary> /// Method converts renewal location from route into GPFeature. /// </summary> /// <param name="loc">Location to convert.</param> /// <param name="route">Route on which location assigned.</param> /// <returns>Renewals GPFeature.</returns> private GPFeature _ConvertRenewal(Location loc, Route route) { Debug.Assert(loc != null); Debug.Assert(route != null); GPFeature feature = new GPFeature(); // Attributes. AttrDictionary attrs = new AttrDictionary(); // Route name. attrs.Add(NAAttribute.ROUTE_NAME, route.Id.ToString()); // Depot name. attrs.Add(NAAttribute.DEPOT_NAME, loc.Id.ToString()); // Service time. attrs.Add(NAAttribute.SERVICE_TIME, route.TimeAtRenewal); // Sequences: currently sequences are not used. feature.Attributes = attrs; return feature; }
/// <summary> /// Method converts pickup and delivery orders into GPFeature. /// </summary> /// <param name="pickupOrder">Order.</param> /// <param name="deliveryOrder">Order.</param> /// <returns>OrderPair GPFeature.</returns> private GPFeature _ConvertOrderPair(Order pickupOrder, Order deliveryOrder) { Debug.Assert(pickupOrder != null && deliveryOrder != null); GPFeature feature = new GPFeature(); // Attributes. AttrDictionary attrs = new AttrDictionary(); // Pickup Name. attrs.Add(NAAttribute.FIRST_ORDER_NAME, pickupOrder.Id.ToString()); // Delivery Name. attrs.Add(NAAttribute.SECOND_ORDER_NAME, deliveryOrder.Id.ToString()); // not using MaxTransitTime feature.Attributes = attrs; return feature; }
/////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Builds stop features. /// </summary> /// <param name="stops">Stops.</param> /// <returns>Created route stops recordset for stops.</returns> private RouteStopsRecordSet _BuildStopFeatures(IList<StopData> stops) { Debug.Assert(stops != null); // Sort stops respecting sequence. var sortedStops = new List<StopData>(stops); SolveHelper.SortBySequence(sortedStops); Debug.Assert(_context != null); SolveHelper.ConsiderArrivalDelayInStops( _context.SolverSettings.ArriveDepartDelay, sortedStops); // Format impedance attribute name. string impedanceAttrName = null; if (!string.IsNullOrEmpty(_context.NetworkDescription.ImpedanceAttributeName)) { impedanceAttrName = string.Format(IMPEDANCE_ATTR_FORMAT, _context.NetworkDescription.ImpedanceAttributeName); } var features = new List<GPFeature>(); for (int index = 0; index < sortedStops.Count; ++index) { var feature = new GPFeature(); // Attributes. feature.Attributes = new AttrDictionary(); StopData sd = sortedStops[index]; Guid objectId = Guid.Empty; if (sd.AssociatedObject != null) objectId = sd.AssociatedObject.Id; feature.Attributes.Add(NAAttribute.NAME, objectId.ToString()); feature.Attributes.Add(NAAttribute.ROUTE_NAME, sd.RouteId.ToString()); // Effective time window. DateTime? twStart = null; DateTime? twEnd = null; _GetEffectiveTW(sd, out twStart, out twEnd); // NOTE: ignore result feature.Attributes.Add(NAAttribute.TW_START, _FormatStopTime(twStart)); feature.Attributes.Add(NAAttribute.TW_END, _FormatStopTime(twEnd)); // Service time. if (impedanceAttrName != null) feature.Attributes.Add(impedanceAttrName, sd.TimeAtStop); var geometry = new GeometryHolder(); geometry.Value = sd.Geometry; if (sd.StopType == StopType.Lunch) { var actualStop = SolveHelper.GetActualLunchStop(sortedStops, index); geometry.Value = actualStop.Geometry; } // Set curb approach. var curbApproach = CurbApproachConverter.ToNACurbApproach( _context.SolverSettings.GetOrderCurbApproach()); if (sd.StopType == StopType.Location) { curbApproach = CurbApproachConverter.ToNACurbApproach( _context.SolverSettings.GetDepotCurbApproach()); } feature.Attributes.Add(NAAttribute.CURB_APPROACH, (int)curbApproach); feature.Geometry = geometry; features.Add(feature); } var rs = new RouteStopsRecordSet(); rs.Features = features.ToArray(); // TODO: will be changed later when support custom AddLocations tool rs.DoNotLocateOnRestrictedElements = _context.SolverSettings.ExcludeRestrictedStreets; return rs; }