如何从Google OR-Tools VRP解决方案中获取实际位置索引与经纬度
解决方案实现步骤
1. 传递位置列表到打印方法
当前PrintSolution方法无法访问VRP方法中创建的locations列表,需将其作为参数传入。修改VRP方法内的调用代码:
if (solution is not null) { PrintSolution(routing, manager, solution, vehicles.Count, locations); }
同时更新PrintSolution的方法签名:
static void PrintSolution(in RoutingModel routing, in RoutingIndexManager manager, in Assignment solution, in int count, List<Location> locations)
2. 修改PrintSolution方法,打印完整位置详情与经纬度
在遍历车辆路线时,通过节点索引匹配对应的Location对象,输出地址、经纬度,同时补充时间、容量等关键信息:
static void PrintSolution(in RoutingModel routing, in RoutingIndexManager manager, in Assignment solution, in int count, List<Location> locations) { Console.WriteLine($"目标值 {solution.ObjectiveValue()}:"); // 打印被跳过的节点 string droppedNodes = "被跳过的节点:"; for (int index = 0; index < routing.Size(); ++index) { if (routing.IsStart(index) || routing.IsEnd(index)) { continue; } if (solution.Value(routing.NextVar(index)) == index) { int nodeId = manager.IndexToNode(index); var loc = locations[nodeId]; droppedNodes += $" [{nodeId}] {loc.Address} (Lng:{loc.Lng}, Lat:{loc.Lat})"; } } Console.WriteLine(droppedNodes); // 获取维度对象,用于打印额外信息 RoutingDimension distanceDim = routing.GetMutableDimension("Distance"); RoutingDimension timeDim = routing.GetMutableDimension("Time"); RoutingDimension capacityDim = routing.GetMutableDimension("Capacity"); long totalDistance = 0; long totalTime = 0; for (int i = 0; i < count; ++i) { Console.WriteLine($"\n车辆 {i} 的路线:"); long routeDistance = 0; var index = routing.Start(i); // 打印起点信息 int startNode = manager.IndexToNode((int)index); var startLoc = locations[startNode]; long startTime = solution.Value(timeDim.CumulVar(index)); long startCapacity = solution.Value(capacityDim.CumulVar(index)); Console.WriteLine($"起点: [{startNode}] {startLoc.Address} (Lng:{startLoc.Lng}, Lat:{startLoc.Lat})"); Console.WriteLine($" 出发时间: {DateTimeOffset.FromUnixTimeSeconds(startTime).LocalDateTime}"); Console.WriteLine($" 当前容量: {startCapacity}"); while (routing.IsEnd(index) == false) { var previousIndex = index; index = solution.Value(routing.NextVar(index)); int prevNode = manager.IndexToNode((int)previousIndex); int currNode = manager.IndexToNode((int)index); var prevLoc = locations[prevNode]; var currLoc = locations[currNode]; // 计算路段距离 long arcDistance = routing.GetArcCostForVehicle(previousIndex, index, i); routeDistance += arcDistance; // 获取当前节点的时间与容量数据 long arrivalTime = solution.Value(timeDim.CumulVar(index)); long currentCapacity = solution.Value(capacityDim.CumulVar(index)); Console.WriteLine($"\n[{prevNode}] {prevLoc.Address} -> [{currNode}] {currLoc.Address}"); Console.WriteLine($" 路段距离: {arcDistance}m"); Console.WriteLine($" 到达时间: {DateTimeOffset.FromUnixTimeSeconds(arrivalTime).LocalDateTime}"); Console.WriteLine($" 当前容量: {currentCapacity}"); Console.WriteLine($" 坐标: Lng={currLoc.Lng}, Lat={currLoc.Lat}"); } // 打印终点信息 int endNode = manager.IndexToNode((int)index); var endLoc = locations[endNode]; long endTime = solution.Value(timeDim.CumulVar(index)); long endCapacity = solution.Value(capacityDim.CumulVar(index)); Console.WriteLine($"\n终点: [{endNode}] {endLoc.Address} (Lng:{endLoc.Lng}, Lat:{endLoc.Lat})"); Console.WriteLine($" 到达时间: {DateTimeOffset.FromUnixTimeSeconds(endTime).LocalDateTime}"); Console.WriteLine($" 当前容量: {endCapacity}"); long routeTime = endTime - startTime; totalDistance += routeDistance; totalTime += routeTime; Console.WriteLine($"\n车辆 {i} 路线总距离: {routeDistance}m"); Console.WriteLine($"车辆 {i} 路线总耗时: {TimeSpan.FromSeconds(routeTime)}"); } Console.WriteLine($"\n所有路线总距离: {totalDistance}m"); Console.WriteLine($"所有路线总耗时: {TimeSpan.FromSeconds(totalTime)}"); }
3. 确认Location类包含经纬度属性
确保你的Location类已定义经度和纬度字段:
public class Location { public string Address { get; set; } public double Lng { get; set; } // 经度 public double Lat { get; set; } // 纬度 // 其他业务属性... }
核心逻辑说明
- 通过
manager.IndexToNode(index)将路由索引转换为节点ID,再从locations列表中匹配对应位置对象,直接获取经纬度信息。 - 新增了时间窗实际到达时间、容量变化、路段距离等维度数据,让解决方案详情更全面。
- 被跳过的节点也会打印位置信息,便于后续问题排查。
内容的提问来源于stack exchange,提问作者DPTP
相关产品推荐
相关产品推荐

