Python lat/long midpoint calculation gives wrong result when longitude > 90

删除回忆录丶 提交于 2019-12-07 08:25:17

问题


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?


回答1:


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:

  1. Input lat/lon in degrees or radians?
  2. Check input lat/lon for valid range
  3. 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)




回答2:


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.




回答3:


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!