Geoalchemy2 запросить всех пользователей в X meteres

у меня есть приложение, которое принимает адресную строку, отправляет ее в Google Maps API и получает координаты lat/long, затем я хочу показать всех пользователей в пределах X метров этой точки (там 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'))
)

я получаю свой lat / long из Google API и храню его в своей базе данных, поэтому из адресной строки "London England" я get

POINT (-0.1198244000000000 51.5112138999999871)

это хранит в своей базе данных, как:

0101000000544843D7CFACBEBF5AE102756FC14940

теперь, когда все работает нормально, теперь, читая документы Geoalchemy2, я не могу найти запрос exmaple для решения моей проблемы.

то, что я хочу передать,-это еще один набор координат lat/long для Geoalchemy2, а затем вернуть ближайших пользователей 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 ответов


ответ:

единицы были в радиусе градуса, поэтому мне пришлось конвертировать мили в дегресс, чтобы получить лучшую (грубую) оценку. Это не должно быть точным:

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()

данные 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) может быть одета к вашим потребностям.