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

好久不见. 提交于 2019-12-05 12:50:18

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)

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