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

Другие вопросы по тегам