Skip to main content
Added final deployment code
Source Link
kickert
  • 1.9k
  • 8
  • 24

The answer from @creidhne using IndexPosition and GeoDistanceList is elegant and works for a vast majority of cases. However, in instances where the spacing between route points is significant (up to half a mile in real world worst case scenarios), and the nearest route point is further up the trail, the reported distance will be over by twice the distance from the waypoint to the route point since you are essentially calculating the distance to double back.

Using the code from @creidhne as a base, I was able to modify it so that it finds the nearest route point prior to the waypoint and then adds the remaining distance. This should give the right answer 100% of the time since it removes any chance of reporting distance from doubling back from a far off route point up the trail.

Here is the complete code:

Bring in the data:

ClearAll[data];
ToExpression@Import["https://pastebin.com/raw/8KDcvMex", "String"];
trail = GeoPosition /@ data[[All, 1]];
waypoint = GeoPosition[{34.56544401, -77.90251801}];

Calculate the Distance

priorpoint = 
Min[PositionIndex[trail] /@ GeoNearest[trail, waypoint, 2]];
Total@GeoDistanceList[trail[[1 ;; priorpoint]]] + 
 UnitConvert[GeoDistance[waypoint, trail[[priorpoint]]], "Miles"]

Note: I had to include UnitConvert since my answer was coming out in feet instead of miles.

EDIT (to add full deployment)

As I mentioned in my initial question, I was actually looking at performing this calculation for a list of waypoints. Below is the full code deployment using an imported GPX file and an XLSX file where Waypoint IDs were in column 6, Lats were in column 12 and Longs were in Column 13. It can then be downloaded as a CSV file.

gpx = Import[
  "route.gpx", "XML"]; 
data = 
 Cases[gpx, 
  XMLElement["trkpt", {"lat" -> lat_, "lon" -> lon_}, 
    other_] :> {ToExpression /@ {lat, lon}}, \[Infinity]]; 
trail = 
 GeoPosition /@ data[[All, 1]];

waypointdata = 
 Import["waypoints.xlsx", {"Data"}][[1, All, {6, 12, 13}]] //
   Rest; ids = waypointdata[[All, 1]];
lat = waypointdata[[All, 2]];
long = waypointdata[[All, 3]]; 
locations = 
 GeoPosition /@ waypointdata[[All, {2, 3}]];

distances = 
  QuantityMagnitude[
   UnitConvert[
    Total[GeoDistanceList[
         trail[[1 ;; 
           Min[PositionIndex[trail] /@ GeoNearest[trail, #, 2]]]]]] + 
       GeoDistance[#, 
        trail[[ Min[
          PositionIndex[trail] /@ GeoNearest[trail, #, 2]]]]] & /@ 
     locations, "Miles"]];

thread = Prepend[Thread[{ids, distances}], {"ID", "Distance"}];

Export["distances.csv", thread, "CSV"]

The answer from @creidhne using IndexPosition and GeoDistanceList is elegant and works for a vast majority of cases. However, in instances where the spacing between route points is significant (up to half a mile in real world worst case scenarios), and the nearest route point is further up the trail, the reported distance will be over by twice the distance from the waypoint to the route point since you are essentially calculating the distance to double back.

Using the code from @creidhne as a base, I was able to modify it so that it finds the nearest route point prior to the waypoint and then adds the remaining distance. This should give the right answer 100% of the time since it removes any chance of reporting distance from doubling back from a far off route point up the trail.

Here is the complete code:

Bring in the data:

ClearAll[data];
ToExpression@Import["https://pastebin.com/raw/8KDcvMex", "String"];
trail = GeoPosition /@ data[[All, 1]];
waypoint = GeoPosition[{34.56544401, -77.90251801}];

Calculate the Distance

priorpoint = 
Min[PositionIndex[trail] /@ GeoNearest[trail, waypoint, 2]];
Total@GeoDistanceList[trail[[1 ;; priorpoint]]] + 
 UnitConvert[GeoDistance[waypoint, trail[[priorpoint]]], "Miles"]

Note: I had to include UnitConvert since my answer was coming out in feet instead of miles.

The answer from @creidhne using IndexPosition and GeoDistanceList is elegant and works for a vast majority of cases. However, in instances where the spacing between route points is significant (up to half a mile in real world worst case scenarios), and the nearest route point is further up the trail, the reported distance will be over by twice the distance from the waypoint to the route point since you are essentially calculating the distance to double back.

Using the code from @creidhne as a base, I was able to modify it so that it finds the nearest route point prior to the waypoint and then adds the remaining distance. This should give the right answer 100% of the time since it removes any chance of reporting distance from doubling back from a far off route point up the trail.

Here is the complete code:

Bring in the data:

ClearAll[data];
ToExpression@Import["https://pastebin.com/raw/8KDcvMex", "String"];
trail = GeoPosition /@ data[[All, 1]];
waypoint = GeoPosition[{34.56544401, -77.90251801}];

Calculate the Distance

priorpoint = 
Min[PositionIndex[trail] /@ GeoNearest[trail, waypoint, 2]];
Total@GeoDistanceList[trail[[1 ;; priorpoint]]] + 
 UnitConvert[GeoDistance[waypoint, trail[[priorpoint]]], "Miles"]

Note: I had to include UnitConvert since my answer was coming out in feet instead of miles.

EDIT (to add full deployment)

As I mentioned in my initial question, I was actually looking at performing this calculation for a list of waypoints. Below is the full code deployment using an imported GPX file and an XLSX file where Waypoint IDs were in column 6, Lats were in column 12 and Longs were in Column 13. It can then be downloaded as a CSV file.

gpx = Import[
  "route.gpx", "XML"]; 
data = 
 Cases[gpx, 
  XMLElement["trkpt", {"lat" -> lat_, "lon" -> lon_}, 
    other_] :> {ToExpression /@ {lat, lon}}, \[Infinity]]; 
trail = 
 GeoPosition /@ data[[All, 1]];

waypointdata = 
 Import["waypoints.xlsx", {"Data"}][[1, All, {6, 12, 13}]] //
   Rest; ids = waypointdata[[All, 1]];
lat = waypointdata[[All, 2]];
long = waypointdata[[All, 3]]; 
locations = 
 GeoPosition /@ waypointdata[[All, {2, 3}]];

distances = 
  QuantityMagnitude[
   UnitConvert[
    Total[GeoDistanceList[
         trail[[1 ;; 
           Min[PositionIndex[trail] /@ GeoNearest[trail, #, 2]]]]]] + 
       GeoDistance[#, 
        trail[[ Min[
          PositionIndex[trail] /@ GeoNearest[trail, #, 2]]]]] & /@ 
     locations, "Miles"]];

thread = Prepend[Thread[{ids, distances}], {"ID", "Distance"}];

Export["distances.csv", thread, "CSV"]
Source Link
kickert
  • 1.9k
  • 8
  • 24

The answer from @creidhne using IndexPosition and GeoDistanceList is elegant and works for a vast majority of cases. However, in instances where the spacing between route points is significant (up to half a mile in real world worst case scenarios), and the nearest route point is further up the trail, the reported distance will be over by twice the distance from the waypoint to the route point since you are essentially calculating the distance to double back.

Using the code from @creidhne as a base, I was able to modify it so that it finds the nearest route point prior to the waypoint and then adds the remaining distance. This should give the right answer 100% of the time since it removes any chance of reporting distance from doubling back from a far off route point up the trail.

Here is the complete code:

Bring in the data:

ClearAll[data];
ToExpression@Import["https://pastebin.com/raw/8KDcvMex", "String"];
trail = GeoPosition /@ data[[All, 1]];
waypoint = GeoPosition[{34.56544401, -77.90251801}];

Calculate the Distance

priorpoint = 
Min[PositionIndex[trail] /@ GeoNearest[trail, waypoint, 2]];
Total@GeoDistanceList[trail[[1 ;; priorpoint]]] + 
 UnitConvert[GeoDistance[waypoint, trail[[priorpoint]]], "Miles"]

Note: I had to include UnitConvert since my answer was coming out in feet instead of miles.