3

I am representing run data as Shapely LineStrings where each Point in the LineString is a coordinate. I am trying to figure out the length of the LineString in terms of miles. I know that LineString has a length method, however I don't know what units the results are.

For instance, I have a run that I know is 0.13 miles but when I print out runs[0].length I get 0.00198245721108. I assume it's because LineString is in the Cartesian coordinate system, but I'm not entirely sure.

steveclark
  • 537
  • 9
  • 27

1 Answers1

3

Shapely's LineString class provides a coords method that returns all of the coordinates that make up the LineString. For example:

from shapely.geometry import LineString

# Create a LineString to mess around with
coordinates = [(0, 0), (1, 0)]
line1 = LineString(coordinates)

# Grab the second coordinate along with its x and y values using standard array indexing
secondCoord = line1.coords[1]
x2 = secondCoord[0]
y2 = secondCoord[1]

# Print values to console to verify code worked
print "Second Coordinate: " + str(secondCord)
print "Second x Value: " + str(x2)
print "Second y Value: " + str(y2)

will print

Second Coordinate: (1.0, 0.0)
Second x Value: 1.0
Second y Value: 0.0

You can use this to grab the lat and lon values of each GPS coordinate in your LineString where x represents lat and y represents lon. Then using the Haversine Formula you can calculate the geographic distance. After a quick search I found this answer that provides Python code for a Haversine Formula function, which I have verified works. However, this just gives you the distance between 2 points, so if your GPS data has turns in it you will have to calculate the distance between each individual point and not the distance of the start and end point. Here is the code I used:

from shapely.geometry import LineString
from math import radians, cos, sin, asin, sqrt

# Calculates distance between 2 GPS coordinates
def haversine(lat1, lon1, lat2, lon2):
    """
    Calculate the great circle distance between two points 
    on the earth (specified in decimal degrees)
    """
    # convert decimal degrees to radians 
    lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])

    # haversine formula 
    dlon = lon2 - lon1 
    dlat = lat2 - lat1 
    a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
    c = 2 * asin(sqrt(a)) 
    r = 3956 # Radius of earth in kilometers. Use 3956 for miles
    return c * r

for line in listOfLines:
    numCoords = len(line.coords) - 1
    distance = 0
    for i in range(0, numCoords):
        point1 = line.coords[i]
        point2 = line.coords[i + 1]
        distance += haversine(point1[0], point1[1], point2[0], point2[1])

    print distance

If you are only doing this for one LineString you can get rid of the outer for loop, but I need to calculate the distance of several runs. Also, note that if you get your code from the answer in the link I have switched around the function parameters because the provided answer had lon first which works but is annoying to have to type haversine(point1[1], point1[0]...)

Suraj Nair
  • 3
  • 1
  • 2
steveclark
  • 537
  • 9
  • 27
  • The mean radius of the earth is 6371 kilometers or 3959 miles in GRS80. What is `r = 3956` in this code? See derived geometric constant R1 here: http://geoweb.mit.edu/~tah/12.221_2005/grs80_corr.pdf – Martin Burch Apr 10 '19 at 10:40