Geoalchemy2查询X米内的所有用户

时间:2013-12-27 16:12:46

标签: python sqlalchemy postgis shapely geoalchemy

我有一个应用程序,它接收地址字符串,将其发送到Google Maps API并获得纬度/经度坐标,然后我想显示此点的X meteres内的所有用户(lat / long存储在我的数据库),然后我想过滤结果只显示有某些宠物的用户

首先,我有我的模型

class User(UserMixin, Base):
    first_name = Column(Unicode)

    address = Column(Unicode)
    location = Column(Geometry('POINT'))

    pets = relationship('Pet', secondary=user_pets, backref='pets') 

class Pet(Base):
    __tablename__ = 'pets'   
    id = Column(Integer, primary_key=True)
    name = Column(Unicode)

user_pets = Table('user_pets', Base.metadata,
    Column('user_id', Integer, ForeignKey('users.id')),
    Column('pet_id', Integer, ForeignKey('pets.id'))
)

我从谷歌API获取我的lat / long并将其存储在我的数据库中,所以从地址字符串“伦敦英格兰”我得到

POINT (-0.1198244000000000 51.5112138999999871)

这存储在我的数据库中,如:

0101000000544843D7CFACBEBF5AE102756FC14940

现在一切正常,现在阅读Geoalchemy2文档,我似乎无法找到解决问题的常见问题。

我想传递的是Geoalchemy2的另一组lat / long坐标,然后返回最近的10个用户。在查询时我也会过滤掉只有某些宠物的用户(这对我的查询起作用并不重要,但我想展示查询实际上会做什么)。

我不想在没有提供示例查询的情况下回答问题,但我真的不知道应该使用哪些函数来实现我所需的结果。

我猜我需要使用“ST_DWithin”或“ST_DFullyWithin”,但我找不到任何一个函数的完整示例。感谢的。

所以我知道有一个有效的查询

distance = 10
address_string = "London, England"
results = Geocoder.geocode(address_string)
# load long[1], lat[0] into shapely
center_point = Point(results.coordinates[1], results.coordinates[0])
print center_point
# 'POINT (-0.1198244000000000 51.5112138999999871)'
wkb_element = from_shape(center_point)
users = DBSession.query(User).\
    filter(func.ST_DWithin(User.location,  wkb_element, distance)).all()

生成以下SQL

2013-12-30 15:12:06,445 INFO  [sqlalchemy.engine.base.Engine][Dummy-2] SELECT users.first_name AS users_first_name, users.last_name AS users_last_name, users.phone AS users_phone, users.address AS users_address, users.about AS users_about, ST_AsBinary(users.location) AS users_location, users.profile_image_id AS users_profile_image_id, users.searchable AS users_searchable, users.user_password AS users_user_password, users.registered_date AS users_registered_date, users.id AS users_id, users.last_login_date AS users_last_login_date, users.status AS users_status, users.user_name AS users_user_name, users.email AS users_email, users.security_code AS users_security_code 
FROM users 
WHERE ST_DWithin(users.location, ST_GeomFromWKB(%(ST_GeomFromWKB_1)s, %(ST_GeomFromWKB_2)s), %(param_1)s)
2013-12-30 15:12:06,445 INFO  [sqlalchemy.engine.base.Engine][Dummy-2] {'ST_GeomFromWKB_1': <read-only buffer for 0x7f7d10258f70, size -1, offset 0 at 0x7f7d10258db0>, 'param_1': 10, 'ST_GeomFromWKB_2': -1}

现在这总是会返回我的所有用户,无论距离变量如何,所以我猜测某些事情并不完全正确,但我无法解决原因。

2 个答案:

答案 0 :(得分:8)

答案:

单位是半径,所以我必须将里程转换为degress以获得最佳(粗略)估计。它不需要准确:

d = 90
distance = d * 0.014472
#1 mile = 0.014472 degrees  

r1 = -0.1198244
r2 = 51.5112139

# load long[1], lat[0] into shapely
center_point = Point(r1, r2)
# 'POINT (-0.1198244000000000 51.5112138999999871)'

wkb_element = from_shape(center_point)

users = DBSession.query(User).\
    filter(func.ST_DFullyWithin(User.location,  wkb_element, distance)).all()

答案 1 :(得分:2)

Lat / Lon数据不适合距离计算。

每个纬度相距约69英里(111公里)。范围从赤道的68.703英里(110.567公里)到极地的69.407(111.699公里)不等(由于地球略呈椭圆形)。这很方便,因为每分钟(1/60度)大约是一英里。

赤道的经度在69.172英里(111.321)处最宽,在极地逐渐收缩到零。在北纬40度以北,经度为53英里(85公里)。

您可以使用ST_Transform将坐标转换为使用米或英里的不同投影。这些确实是局部的,因为它们将地球的球体投射到光栅化的平面上。英国国家电网(SRID 27700)可能适合您的需求。