SqlAlchemy / Sqlite Расчет расстояния - PullRequest
3 голосов
/ 06 марта 2011

Я использую sqlAlchemy ORM и хотел бы рассчитать и вернуть расстояние от заданной точки и сохраненных точек.

class Event(Base):

    __tablename__ = 'events'
    # Schema
    id = Column(Integer, primary_key=True)
    title = Column(String(150))
    description = Column(Text)
    url = Column(String(800))
    lat = Column(Float)
    lng = Column(Float)

.... и мой запрос:

    nearest = """SELECT *, ((lat - '-41.288889') * (lat - '-41.288889')
 + (lng  - 174.777222) * (lng  - 174.777222)) AS distance FROM events 
ORDER BY distance ASC """

e = Event.query.from_statement(nearest)

Похоже, что это возвращает объекты в правильном порядке, но у меня нет доступа к атрибуту расстояния.Как мне получить доступ к этому значению или каков наилучший способ достижения этого?

Ответы [ 3 ]

2 голосов
/ 06 марта 2011

Проблема 1: градусы широты и градуса долготы имеют разную длину ... то же самое на экваторе (если земля представляет собой сферу), но на полюсах градус долготы имеет нулевую длину.

Обновление Чтобы привести пример серьезности вашей проблемы:

Путешествие на север или юг на один градус широты охватит около 111,2 км (при условии, что Земля является сферой).Путешествие на восток или запад на один градус долготы займет около 111,2 км на экваторе .На широте -41,3 градуса она охватит всего 83,5 км.

От (-41,3, 174,8) к востоку до (-41,3, 174,92) составляет 10,0 км.Ваш расчет обрабатывает его так, как если бы он находился на расстоянии 13,3 км - ошибка составляет 33% .

Вы можете получить погрешность в 4 метра довольно простым приближением:

from math import pi, sqrt, radians, cos
Earth_radius_km = 6371.009
km_per_deg_lat = 2 * pi * Earth_radius_km / 360.0

# what your SQL query is in effect doing
def approx_dist_1(lat1, lon1, lat2, lon2):
    return km_per_deg_lat * sqrt((lat1 - lat2) ** 2 + (lon1 - lon2) ** 2)

# better version    
def approx_dist_2(lat1, lon1, lat2, lon2):
    # calculate km_per_deg_lon for your central station in Python and 
    # embed it in your query
    km_per_deg_lon = km_per_deg_lat * cos(radians(lat1))
    return sqrt((km_per_deg_lat *(lat1 - lat2)) ** 2 + (km_per_deg_lon * (lon1 - lon2)) ** 2)

Это приблизительное расстояние достаточно для приложений «найди ближайшую пиццерию» и имеет преимущество в том, что его можно использовать в обычных средах, таких как SQLite, которые не поддерживают sin / cos / tan и их инверсиииз коробки.

Проблема 2: SQLite терпим, но вы не должны использовать привычку использовать кавычки, как в (lat - '-41.288889')

Проблема 3: Я не могу воспроизвести вашу проблемуна уровне SQLlite:

sqlite> create table foo (lat float, lon float);
sqlite> insert into foo values(99.9, -170.1);
sqlite> select * from foo;
99.9|-170.1
sqlite> SELECT *, ((lat - '-41.288889') * (lat - '-41.288889')
   ...>  + (lon  - 174.777222) * (lon  - 174.777222)) AS distance from foo;
99.9|-170.1|138874.600631492
sqlite>

Возможно, вам следует уточнить, что «Это, похоже, возвращает объекты в правильном порядке, но у меня нет доступа к атрибуту расстояния» ... что делает самоанализ e сказать тебе?

0 голосов
/ 10 марта 2011

Как указывал Джон Мачин, ваш алгоритм расстояний немного странный и приведет к неверным результатам, если ваши точки находятся на расстоянии, примерно, 100 км друг от друга.и т. д.), чтобы получить разумный результат.Ищите алгоритмы Haversine или Vincenty.Лучше посмотрите, есть ли в SQLAlchemy что-то полезное для вас.

0 голосов
/ 10 марта 2011

Справку можно найти здесь: Столбец настраиваемого запроса SQLAlchemy

...