Выполнение запроса геохэша в диапазоне памяти

Прежде всего я хотел бы сказать, что я не заинтересован в использовании Redis или любой другой пространственной БД. Я пытаюсь сделать очень упрощенный запрос диапазона геохэша в памяти, и я использую следующее программное обеспечение для вычисления геохэша: geohash-int C, и у меня есть оболочка Cython для вызова этих API в Python 3.6. Я использую SortedList для хранения геохэшей, и моя цель — создать простой диапазон геохэшей запрос в памяти.

#GeoHash is a Cython wrapper of external C geohash library (link provided)
from geo import GeoHash
from sortedcontainers import SortedList
import numpy as np

import time
minLat = 27.401436
maxLat = 62.54858
minLo = -180.0
maxLo = 179.95000000000002
latGrid = np.arange(minLat,maxLat,0.05)
lonGrid = np.arange(minLo,maxLo,0.05)
geoHash = GeoHash()

print(latGrid.shape,lonGrid.shape)
gridLon,gridLat = np.meshgrid(lonGrid,latGrid)
grid_points = np.c_[gridLon.ravel(),gridLat.ravel()]

sl = SortedList()
geohash1 = {}
t0 = time.time()
for grid_point in grid_points:
   lon = grid_point[0]
   lat = grid_point[1]
   geohash = geoHash.encode(lon,lat,26)
   bitsOriginal = geohash["bits"]
   sl.add(bitsOriginal)
   neighbors = geoHash.get_neighbors(geohash)
   for k,v in neighbors.items():
        bits1 = v["bits"]
        sl.add(bits1)
t1 = time.time()
print(t1-t0)
lonTest = 172.76843
latTest = 61.560745
geohashTest = geoHash.encode(lonTest,latTest,26)
bitsTest =    geohashTest["bits"]

Я хочу сделать следующее

it = sl.irange(bitsTest-window,bitsTest+window)

Мой вопрос: как мне рассчитать окно? Я хочу, чтобы окно было в пределах 0,1 градуса или любого другого окна, которое я укажу. Я понятия не имею, как рассчитать окно. Весь пакет geohash работает очень быстро, и меня интересуют только приблизительные совпадения для моего запроса. Я считаю, что моя контрольная точка должна находиться в диапазоне набора входных данных, для которого я рассчитал геохэши, но я понятия не имею, как получить диапазон геохэшей для моей точки запроса. Может кто-нибудь помочь?

ОБНОВЛЕНИЕ Предлагаемый ответ хорош, но имеет сложность O(N). Если существует сложность порядка O(log N), это было бы приемлемо.


person gansub    schedule 14.09.2018    source источник
comment
Почему вы добавляете всех соседей?   -  person GrantJ    schedule 15.09.2018
comment
В вашем примере кода было бы намного быстрее сначала построить несортированный список геохэшей, а затем создать SortedList из несортированного списка. Существует быстрый путь для инициализации пустого SortedList из несортированных значений.   -  person GrantJ    schedule 15.09.2018


Ответы (2)


Похоже, это должно быть возможно. Вам нужна точность 0,1 градуса. Конечно, сколько это в метрах, зависит от того, где вы находитесь на планете и говорим ли мы о долготе или широте. Но это можно посчитать. Основываясь на этом, вы можете выяснить, каким должен быть минимальный префикс вашего геша, чтобы его прямоугольная форма покрывала это. Более длинные хэши с тем же префиксом содержатся в прямоугольнике, который описывает меньший префикс.

Для большей детализации используйте несколько более длинных прямоугольников. Это также поможет вам охватить случаи, когда любой диапазон, на который вы смотрите, пересекает край вашего прямоугольника.

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

Вы можете проверить мою библиотеку https://github.com/jillesvangurp/geogeometry. Он имеет алгоритмы и функции для всего вышеперечисленного. Вы можете делать круги, ограничивающие рамки или многоугольники и покрывать их геохэшами заданной максимальной длины. Вы можете вычислить подходящее значение для этой максимальной длины с помощью другой функции.

Он основан на Java, но он должен легко переноситься на Python или что-то еще, что вы хотите, учитывая, как я его структурировал. В основном это просто циклы и простая математика с использованием двойников.

На самом деле я использовал это для реализации простой геопространственной поисковой системы шесть лет назад. Масштабируется достаточно хорошо, если у вас есть база данных или поисковая система, которая может обрабатывать десятки миллионов гехехешей. Для небольших наборов данных вы можете легко сделать это в памяти.

person Jilles van Gurp    schedule 14.09.2018

Геохэши спроектированы таким образом, что два местоположения, которые находятся рядом друг с другом, будут иметь одинаковый префикс/значение. Википедия описывает алгоритм с примером. Насколько я понимаю, широта и долгота преобразуются в двоичные значения, а биты чередуются друг с другом. Например:

In [33]: def geohash(lat, lng):
    ...:     "Approximate geohash algorithm."
    ...:     # Step 1: Convert to fixed-point.
    ...:     # I'm going to support six decimal places.
    ...:     lat = int(lat * 1e6)
    ...:     lng = int(lng * 1e6)
    ...:     # Step 2: Convert integers to 32-bit binary.
    ...:     lat = format(lat, '032b')
    ...:     lng = format(lng, '032b')
    ...:     # Step 3: Interleave bits from lat and lng.
    ...:     bits = [bit for pair in zip(lat, lng) for bit in pair]
    ...:     # Step 4: Convert bits to 64-bit integer.
    ...:     return int(''.join(bits), 2)
    ...: 
    ...: 

In [34]: lat, lng = 37.7749, 122.4194  # San Francisco, CA

In [35]: geohash(lat, lng)
Out[35]: 8215849339476576

Если вы немного измените широту и долготу, то число не сильно изменится. Вы можете создать ограничивающую рамку, добавляя и вычитая как широту, так и долготу:

In [38]: sf = geohash(lat, lng)

In [39]: lower_bounds = geohash(lat-0.001, lng-0.001)

In [40]: upper_bounds = geohash(lat+0.001, lng+0.001)

In [41]: lower_bounds < sf < upper_bounds
Out[41]: True

Теперь с нижней и верхней границами вы можете использовать SortedList.irange найти все точки вблизи заданной широты и долготы за время O(log(n)).

person GrantJ    schedule 14.09.2018
comment
Проблема в том, что я не знаю, сколько я буду изменять, так как точкой запроса может быть что угодно. - person gansub; 15.09.2018
comment
Что вы подразумеваете под точкой запроса и изменением? В моем коде, как это связано с широтой и долготой или 0,001? - person GrantJ; 15.09.2018
comment
если вы видите мой пример кода в вопросе - точка запроса и набор справочных данных (для которых я рассчитал геохэши) очень разные. Они не имеют отношения друг к другу. - person gansub; 15.09.2018