Python地理编码按距离过滤

发布于 2024-09-08 10:14:26 字数 673 浏览 2 评论 0原文

我需要过滤地理编码以了解与某个位置的接近程度。例如,我想要过滤餐厅地理编码列表,以识别距我当前位置 10 英里以内的餐厅。

有人可以指点我一个将距离转换为纬度和纬度的函数吗?经度增量?例如:

class GeoCode(object):
   """Simple class to store geocode as lat, lng attributes."""
   def __init__(self, lat=0, lng=0, tag=None):
      self.lat = lat
      self.lng = lng
      self.tag = None

def distance_to_deltas(geocode, max_distance):
   """Given a geocode and a distance, provides dlat, dlng
      such that

         |geocode.lat - dlat| <= max_distance
         |geocode.lng - dlng| <= max_distance
   """
   # implementation
   # uses inverse Haversine, or other function?
   return dlat, dlng

注意:我使用距离的最高范数。

I need to filter geocodes for near-ness to a location. For example, I want to filter a list of restaurant geocodes to identify those restaurants within 10 miles of my current location.

Can someone point me to a function that will convert a distance into latitude & longitude deltas? For example:

class GeoCode(object):
   """Simple class to store geocode as lat, lng attributes."""
   def __init__(self, lat=0, lng=0, tag=None):
      self.lat = lat
      self.lng = lng
      self.tag = None

def distance_to_deltas(geocode, max_distance):
   """Given a geocode and a distance, provides dlat, dlng
      such that

         |geocode.lat - dlat| <= max_distance
         |geocode.lng - dlng| <= max_distance
   """
   # implementation
   # uses inverse Haversine, or other function?
   return dlat, dlng

Note: I am using the supremum norm for distance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

暮倦 2024-09-15 10:14:26

似乎还没有一个好的Python 实现。幸运的是,“相关文章”侧边栏是我们的朋友。 这篇文章< /a> 指向一篇优秀文章,其中提供了数学知识和 Java 实现。您需要的实际函数相当短,并且嵌入在下面我的 Python 代码中。已测试至所示程度。阅读评论中的警告。

from math import sin, cos, asin, sqrt, degrees, radians

Earth_radius_km = 6371.0
RADIUS = Earth_radius_km

def haversine(angle_radians):
    return sin(angle_radians / 2.0) ** 2

def inverse_haversine(h):
    return 2 * asin(sqrt(h)) # radians

def distance_between_points(lat1, lon1, lat2, lon2):
    # all args are in degrees
    # WARNING: loss of absolute precision when points are near-antipodal
    lat1 = radians(lat1)
    lat2 = radians(lat2)
    dlat = lat2 - lat1
    dlon = radians(lon2 - lon1)
    h = haversine(dlat) + cos(lat1) * cos(lat2) * haversine(dlon)
    return RADIUS * inverse_haversine(h)

def bounding_box(lat, lon, distance):
    # Input and output lats/longs are in degrees.
    # Distance arg must be in same units as RADIUS.
    # Returns (dlat, dlon) such that
    # no points outside lat +/- dlat or outside lon +/- dlon
    # are <= "distance" from the (lat, lon) point.
    # Derived from: http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates
    # WARNING: problems if North/South Pole is in circle of interest
    # WARNING: problems if longitude meridian +/-180 degrees intersects circle of interest
    # See quoted article for how to detect and overcome the above problems.
    # Note: the result is independent of the longitude of the central point, so the
    # "lon" arg is not used.
    dlat = distance / RADIUS
    dlon = asin(sin(dlat) / cos(radians(lat)))
    return degrees(dlat), degrees(dlon)

if __name__ == "__main__":

    # Examples from Jan Matuschek's article

    def test(lat, lon, dist):
        print "test bounding box", lat, lon, dist
        dlat, dlon = bounding_box(lat, lon, dist)
        print "dlat, dlon degrees", dlat, dlon
        print "lat min/max rads", map(radians, (lat - dlat, lat + dlat))
        print "lon min/max rads", map(radians, (lon - dlon, lon + dlon))

    print "liberty to eiffel"
    print distance_between_points(40.6892, -74.0444, 48.8583, 2.2945) # about 5837 km
    print
    print "calc min/max lat/lon"
    degs = map(degrees, (1.3963, -0.6981))
    test(*degs, dist=1000)
    print
    degs = map(degrees, (1.3963, -0.6981, 1.4618, -1.6021))
    print degs, "distance", distance_between_points(*degs) # 872 km

There seems not to have been a good Python implementation. Fortunately the SO "Related articles" sidebar is our friend. This SO article points to an excellent article that gives the maths and a Java implementation. The actual function that you require is rather short and is embedded in my Python code below. Tested to extent shown. Read warnings in comments.

from math import sin, cos, asin, sqrt, degrees, radians

Earth_radius_km = 6371.0
RADIUS = Earth_radius_km

def haversine(angle_radians):
    return sin(angle_radians / 2.0) ** 2

def inverse_haversine(h):
    return 2 * asin(sqrt(h)) # radians

def distance_between_points(lat1, lon1, lat2, lon2):
    # all args are in degrees
    # WARNING: loss of absolute precision when points are near-antipodal
    lat1 = radians(lat1)
    lat2 = radians(lat2)
    dlat = lat2 - lat1
    dlon = radians(lon2 - lon1)
    h = haversine(dlat) + cos(lat1) * cos(lat2) * haversine(dlon)
    return RADIUS * inverse_haversine(h)

def bounding_box(lat, lon, distance):
    # Input and output lats/longs are in degrees.
    # Distance arg must be in same units as RADIUS.
    # Returns (dlat, dlon) such that
    # no points outside lat +/- dlat or outside lon +/- dlon
    # are <= "distance" from the (lat, lon) point.
    # Derived from: http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates
    # WARNING: problems if North/South Pole is in circle of interest
    # WARNING: problems if longitude meridian +/-180 degrees intersects circle of interest
    # See quoted article for how to detect and overcome the above problems.
    # Note: the result is independent of the longitude of the central point, so the
    # "lon" arg is not used.
    dlat = distance / RADIUS
    dlon = asin(sin(dlat) / cos(radians(lat)))
    return degrees(dlat), degrees(dlon)

if __name__ == "__main__":

    # Examples from Jan Matuschek's article

    def test(lat, lon, dist):
        print "test bounding box", lat, lon, dist
        dlat, dlon = bounding_box(lat, lon, dist)
        print "dlat, dlon degrees", dlat, dlon
        print "lat min/max rads", map(radians, (lat - dlat, lat + dlat))
        print "lon min/max rads", map(radians, (lon - dlon, lon + dlon))

    print "liberty to eiffel"
    print distance_between_points(40.6892, -74.0444, 48.8583, 2.2945) # about 5837 km
    print
    print "calc min/max lat/lon"
    degs = map(degrees, (1.3963, -0.6981))
    test(*degs, dist=1000)
    print
    degs = map(degrees, (1.3963, -0.6981, 1.4618, -1.6021))
    print degs, "distance", distance_between_points(*degs) # 872 km
<逆流佳人身旁 2024-09-15 10:14:26

这就是使用半正弦公式计算纬度/经度对之间的距离的方法:

import math 

R = 6371 # km
dLat = (lat2-lat1) # Make sure it's in radians, not degrees
dLon = (lon2-lon1) # Idem 
a = math.sin(dLat/2) * math.sin(dLat/2) +
    math.cos(lat1) * math.cos(lat2) * 
    math.sin(dLon/2) * math.sin(dLon/2) 
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a)) 
d = R * c;

现在可以根据阈值测试“d”(也以公里为单位)。如果您想要除公里以外的其他值,请调整半径。

很抱歉,我无法为您提供即插即用的解决方案,但我不理解您的代码骨架(请参阅评论)。

另请注意,现在您可能想使用余弦球面定律而不是半正弦定律。数值稳定性的优势不再值得,而且它非常容易理解、编码和使用。

This is how you calculate distances between lat/long pairs using the haversine formula:

import math 

R = 6371 # km
dLat = (lat2-lat1) # Make sure it's in radians, not degrees
dLon = (lon2-lon1) # Idem 
a = math.sin(dLat/2) * math.sin(dLat/2) +
    math.cos(lat1) * math.cos(lat2) * 
    math.sin(dLon/2) * math.sin(dLon/2) 
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a)) 
d = R * c;

It is now trivial to test "d" (also in km) against your threshold. If you want something else than km, adjust the radius.

I'm sorry I can't give you a drop-in solution, but I do not understand your code skeleton (see comment).

Also note that these days you probably want to use the spherical law of cosines rather than Haversine. The advantages in numerical stability are no longer worth it, and it's a hell of a lot simple to understand, code and use.

自此以后,行同陌路 2024-09-15 10:14:26

如果您将数据存储在 MongoDB 中,它会为您很好地进行索引地理位置搜索,并且优于上面的纯 Python 解决方案,因为它会为您处理优化。

http://www.mongodb.org/display/DOCS/Geospatial+Indexing

If you store data in MongoDB, it does nicely indexed geolocation searches for you, and is superior to the pure-Python solutions above because it will handle optimization for you.

http://www.mongodb.org/display/DOCS/Geospatial+Indexing

意中人 2024-09-15 10:14:26

约翰·梅钦的回答对我帮助很大。只是有一个小错误:在 boundigbox 中交换了纬度和经度:

dlon = distance / RADIUS
dlat = asin(sin(dlon) / cos(radians(lon)))
return degrees(dlat), degrees(dlon)

这解决了问题。原因是经度不会改变每度的距离,但纬度会改变。它们的距离取决于经度。

John Machin's answer helped me much. There is just a small mistake: latitudes and longitudes are swapped in boundigbox:

dlon = distance / RADIUS
dlat = asin(sin(dlon) / cos(radians(lon)))
return degrees(dlat), degrees(dlon)

this solves the problem. The reason is that longitudes don't changes their distance per degree - but latitudes do. Their distance is depending on the longitude.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文