我正在尝试计算在C#中使用GeoCoordinates的所有站点之间的最佳路线。
我有一些方法可以获取列表中的下一个最近位置,但是我需要能够通过以下方式对原始列表进行排序:
start -> next stop -> next stop -> next stop -> finish
这是我目前正在使用的:
模型
public class DispatchStopModel
{
public long OrderDispatchID { get; set; }
public long? NextDispatchID { get; set; }
public PhysicalAddress PhysicalAddress { get; set; }
public double Distance { get; set; }
}
方法
private static double DistanceTo(double lat1, double lon1, double lat2, double lon2, char unit = 'M')
{
double rlat1 = Math.PI * lat1 / 180;
double rlat2 = Math.PI * lat2 / 180;
double theta = lon1 - lon2;
double rtheta = Math.PI * theta / 180;
double dist =
Math.Sin(rlat1) * Math.Sin(rlat2) + Math.Cos(rlat1) *
Math.Cos(rlat2) * Math.Cos(rtheta);
dist = Math.Acos(dist);
dist = dist * 180 / Math.PI;
dist = dist * 60 * 1.1515;
switch (unit)
{
case 'K': //Kilometers -> default
return dist * 1.609344;
case 'N': //Nautical Miles
return dist * 0.8684;
case 'M': //Miles
return dist;
}
return dist;
}
private void FindClosestStop(DispatchStopModel start, ICollection<DispatchStopModel> stops)
{
if(start.PhysicalAddress?.HasGeocode ?? false)
{
foreach(var stop in stops.Where(s => s.OrderDispatchID != start.OrderDispatchID))
{
if(stop.PhysicalAddress?.HasGeocode ?? false)
{
double distanceTo = DistanceTo((double)start.PhysicalAddress.Latitude, (double)start.PhysicalAddress.Longitude, (double)stop.PhysicalAddress.Latitude, (double)stop.PhysicalAddress.Longitude);
if (distanceTo < start.Distance)
{
start.Distance = distanceTo;
start.NextDispatchID = stop.OrderDispatchID;
}
}
}
}
}
现在这是我的控制器动作:
public IActionResult GetDeliveries()
{
var deliveries = deliveryService.GetYourDeliveries();
var Stops = Deliveries.Select(d => new DispatchStopModel
{
OrderDispatchID = d.OrderDispatchID,
NextDispatchID = null,
PhysicalAddress = d.Order.OrderAddress?.PhysicalAddress,
Distance = double.MaxValue
})
.ToArray();
foreach(var stop in Stops)
{
FindClosestStop(stop, Stops);
}
//How can I sort on deliveries using the "NextDispatchID" from the collection I just generated?
}
我当时想的是,我可以设置一个新列表,并在OrderDispatchID等于站点的OrderDispatchID的位置手动插入每个交货。我觉得这样效率很低。
对此有正确的想法吗?
预先感谢
答案 0 :(得分:2)
从高层次看您的问题,在我看来,您实际上正在处理一个链表,其中每个项目都指向下一个项目。
如果是这种情况,则为每个项目设置“下一个”项目的一种方法是:复制所有停靠点,遍历原始停靠点列表,在每次迭代中在列表副本中分配最接近的停靠点,然后从列表副本中删除该项目,以免再次分配该项目:
List<DispatchStopModel> deliveries = deliveryService.GetYourDeliveries();
List<DispatchStopModel> temp = deliveries.ToList();
// Assuming you have a start location
DispatchStopModel start = GetStartingPoint();
// Assign the closest stop to it
FindClosestStop(start, deliveries);
// Now assign the closest delivery for each of the rest
// of the items, removing the closest item on each
// iteration, so it doesn't get assigned more than once.
foreach (var delivery in deliveries)
{
var closest = FindClosestStop(delivery, temp);
temp.Remove(closest);
}
此后,应为所有项目分配一个NextDispatchID
,该{}指向下一个最近的交货点。如果您仍然想对列表进行“排序”,此站点上有许多关于如何对链表进行排序的答案(例如,like this)。
请注意,我对FindClosestStop
进行了一些更改,以使其返回找到的最接近的停靠点:
private DispatchStopModel FindClosestStop(DispatchStopModel start,
ICollection<DispatchStopModel> stops)
{
DispatchStopModel closest = null;
// other code ommitted for brevity
if (distanceTo < start.Distance)
{
start.Distance = distanceTo;
start.NextDispatchID = stop.OrderDispatchID;
closest = stop;
}
// other code ommitted for brevity
return closest;
}
还要注意,Equals
类上的GetHashCode
和DispatchStopModel
应该被重写,以使Remove
方法起作用。如果您不想这样做,则需要搜索具有匹配的OrderDispatchID
的商品并将其删除。