Geoalchemy2 опрашивает всех пользователей в пределах X метров
У меня есть приложение, которое принимает адресную строку, отправляет ее в API Карт Google и получает координаты широты / долготы, затем я хочу показать всех пользователей в пределах X метров этой точки (там широта / долгота хранится в моей базе данных). Затем я хочу отфильтровать результат, чтобы показывать только пользователей с определенными домашними животными.
Итак, во-первых, у меня есть свои модели
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" я получаю
POINT (-0.1198244000000000 51.5112138999999871)
это хранится в моей базе данных, как:
0101000000544843D7CFACBEBF5AE102756FC14940
Теперь, когда все работает нормально, сейчас, читая документы Geoalchemy2, я не могу найти исключительный запрос для решения моей проблемы.
То, что я хочу передать, - это другой набор координат широты / долготы для 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()
Данные широты / долготы не особенно подходят для расчета расстояний.
Каждый градус широты составляет приблизительно 69 миль (111 километров) друг от друга. Диапазон варьируется (из-за слегка эллипсоидальной формы Земли) от 68 703 миль (110 567 км) на экваторе до 69 407 (111 699 км) на полюсах. Это удобно, потому что каждая минута (1/60 градуса) составляет примерно одну милю.
На экваторе широта долготы достигает 69,172 миль (111,321), а на полюсах она постепенно уменьшается до нуля. В 40° к северу или югу расстояние между градусами долготы составляет 53 мили (85 км).
Вы можете использовать ST_Transform для преобразования ваших координат в другую проекцию, которая использует метры или мили. Они имеют тенденцию быть локальными, поскольку они проецируют сферу Земли на растеризованную плоскость. Британская национальная сеть (SRID 27700) может соответствовать вашим потребностям.