I have a problem with a short function to calculate the midpoint of a line when given the latitude and longitude of the points at each end. To put it simply, it works correctly when the longitude is greater than -90 degrees or less than 90 degrees. For the other half of the planet, it provides a somewhat random result.
The code is a python conversion of javascript provided at http://www.movable-type.co.uk/scripts/latlong.html, and appears to conform to the corrected versions here and here. When comparing with the two stackoverflow versions, I'll admit I don't code in C# or Java, but I can't spot where my error is.
Code is as follows:
#!/usr/bin/python
import math
def midpoint(p1, p2):
lat1, lat2 = math.radians(p1[0]), math.radians(p2[0])
lon1, lon2 = math.radians(p1[1]), math.radians(p2[1])
dlon = lon2 - lon1
dx = math.cos(lat2) * math.cos(dlon)
dy = math.cos(lat2) * math.sin(dlon)
lat3 = math.atan2(math.sin(lat1) + math.sin(lat2), math.sqrt((math.cos(lat1) + dx) * (math.cos(lat1) + dx) + dy * dy))
lon3 = lon1 + math.atan2(dy, math.cos(lat1) + dx)
return(math.degrees(lat3), math.degrees(lon3))
p1 = (6.4, 45)
p2 = (7.3, 43.5)
print "Correct:", midpoint(p1, p2)
p1 = (95.5,41.4)
p2 = (96.3,41.8)
print "Wrong:", midpoint(p1, p2)
Any suggestions?
Replace your arg set up code by:
lat1, lon1 = p1
lat2, lon2 = p2
assert -90 <= lat1 <= 90
assert -90 <= lat2 <= 90
assert -180 <= lon1 <= 180
assert -180 <= lon2 <= 180
lat1, lon1, lat2, lon2 = map(math.radians, (lat1, lon1, lat2, lon2))
and run your code again.
Update A few hopefully-helpful general suggestions about calculations involving latitude/longitude:
- Input lat/lon in degrees or radians?
- Check input lat/lon for valid range
- Check OUTPUT lat/lon for valid range. Longitude has a discontinuity at the international dateline.
The last part of the midpoint routine could be usefully changed to avoid a potential problem with long-distance use:
lon3 = lon1 + math.atan2(dy, math.cos(lat1) + dx)
# replacement code follows:
lon3d = math.degrees(lon3)
if lon3d < -180:
print "oops1", lon3d
lon3d += 360
elif lon3d > 180:
print "oops2", lon3d
lon3d -= 360
return(math.degrees(lat3), lon3d)
For example, finding a midpoint between Auckland, New Zealand (-36.9, 174.8) and Papeete, Tahiti (-17.5, -149.5) produces oops2 194.270430902 on the way to a valid answer (-28.355951246746923, -165.72956909809082)
First of all, apologies that I'm leaving another answer. I have a solution to the problem mentioned in that answer involving how to find the midpoint of two points on either side of the dateline. I'd love to simply add a comment to the existing answer, but I don't have the reputation to do so.
The solution was found by looking at the Javascript files powering the tool at http://www.movable-type.co.uk/scripts/latlong.html. I'm using shapely.geometry.Point. If you don't want to install this package, then using tuples instead would work just the same.
def midpoint(pointA, pointB):
lonA = math.radians(pointA.x)
lonB = math.radians(pointB.x)
latA = math.radians(pointA.y)
latB = math.radians(pointB.y)
dLon = lonB - lonA
Bx = math.cos(latB) * math.cos(dLon)
By = math.cos(latB) * math.sin(dLon)
latC = math.atan2(math.sin(latA) + math.sin(latB),
math.sqrt((math.cos(latA) + Bx) * (math.cos(latA) + Bx) + By * By))
lonC = lonA + math.atan2(By, math.cos(latA) + Bx)
lonC = (lonC + 3 * math.pi) % (2 * math.pi) - math.pi
return Point(math.degrees(lonC), math.degrees(latC))
I hope this is helpful and not regarded as inappropriate seeing as how it is an answer to the question raised in the previous answer.
Not straight answer to the >90 question, but it helped in my case so I leave it here just in case it could help others.
I only needed to place the mid point in a map with lat,long in degrees. I used no conversion to radians and it is correctly depicted in the map by using simple euclidean distance. Example function:
def midpoint_euclidean(x1,y1,x2,y2):
dist_x = abs(x1-x2) / 2.
dist_y = abs(y1-y2) / 2.
res_x = x1 - dist_x if x1 > x2 else x2 - dist_x
res_y = y1 - dist_y if y1 > y2 else y2 - dist_y
return res_x, res_y
来源:https://stackoverflow.com/questions/5895832/python-lat-long-midpoint-calculation-gives-wrong-result-when-longitude-90