Используете геохэш для поиска близости?

30

Я ищу, чтобы оптимизировать время гео-поисков.

Я ввожу координаты широты и точки, и я ищу по заранее вычисленному набору местоположений для n ближайших точек.

Мне все равно, сколько времени / пространства займет построение предварительно вычисленного индекса местоположений, но мне все равно, что запросы будут очень быстрыми.

Я думаю об использовании geohash в качестве ключа поиска, где я сначала проверю, получу ли я результаты для X-символов ключа, а затем продолжу обрезать символы от конца ключа до тех пор, пока не начну видеть результаты.

Насколько мне известно (пока очень немного), о методах геоиндекса, этот подход должен дать самые быстрые результаты (с точки зрения времени запроса) по сравнению со всеми другими известными реализациями (такими как R Tree и co.)

Максим Векслер
источник
Есть ли значительная разница между использованием геохеша и хранением широты / долготы в восточных / северных направлениях (например)? Предположительно с обоими вы можете изменить точность поиска, обрезая символы / цифры. (Это просто вопрос из любопытства - я не знаком с этой темой).
DJQ
Эти точки хранятся в базе данных или в памяти или?
Марк Пфистер
@MarcPfister этой проблеме 2 года (для моего варианта использования), но она всегда актуальна для сообщества, поэтому я продолжу активное обсуждение. Обсуждаемые данные действительно хранятся в базе данных nosql.
Максим Векслер
Кроме того, я считаю, что с момента получения ответа на этот вопрос MongoDB успешно внедрила индексацию и поиск по геохэшу, что подтверждает эту точку зрения. Я еще не видел документ по реализации, но код открыт и доступен для любой заинтересованной стороны.
Максим Векслер
Ах хорошо. CouchDB также теперь имеет пространственную индексацию, возможно, также с использованием геохеша.
Марк Пфистер

Ответы:

25

Абсолютно вы можете. И это может быть довольно быстро. (Также могут быть распределены интенсивные биты вычислений)

Есть несколько способов, но один из способов, с которыми я работал, заключается в использовании упорядоченного списка геохешей на основе целых чисел и нахождении всех диапазонов геохешей ближайших соседей для определенного разрешения геохеша (разрешение приближается к вашим distanceкритериям), а затем запрашивая эти диапазоны геохэш, чтобы получить список ближайших точек. Для этого я использую redis и nodejs (т.е. javascript). Redis работает очень быстро и может очень быстро получать упорядоченные диапазоны, но он не может выполнять большую часть действий по обработке запросов индексирования, которые могут выполнять базы данных SQL.

Метод описан здесь: https://github.com/yinqiwen/ardb/wiki/Spatial-Index

Но суть в этом (перефразируя ссылку):

  1. Вы сохраняете все точки с геошахедом в лучшем разрешении, которое вы хотите (макс. Обычно 64-битное целое, если это доступно, или в случае javascript, 52-битное) в упорядоченном наборе (т. Е. Zset в redis). В большинство библиотек геохэш в наши дни встроены целочисленные функции геохэш, и вам нужно будет использовать их вместо более распространенных геохешей base32.
  2. Основываясь на радиусе, в котором вы хотите искать, вам нужно найти битовую глубину / разрешение, которое будет соответствовать вашей области поиска, и это должно быть меньше или равно вашей сохраненной глубине геохэш-бит. У связанного сайта есть таблица, которая соотносит битовую глубину геохеша с площадью ограничительной рамки в метрах.
  3. Затем вы перефразируете исходную координату в этом более низком разрешении.
  4. При этом более низком разрешении также найдите 8 соседних (n, ne, e, se, s, sw, w, nw) областей геохеша. Причина, по которой вам нужно использовать метод соседей, состоит в том, что две координаты, расположенные почти рядом друг с другом, могут иметь совершенно разные геохеша, поэтому вам нужно сделать некоторое усреднение области, охватываемой поиском.
  5. Как только вы получите все соседние геохеши в этом более низком разрешении, добавьте в список геохэш вашей координаты с шага 3.
  6. Затем вам нужно построить диапазон значений геохеша для поиска, которые охватывают эти 9 областей. Значения на шаге 5 - это ваш нижний предел диапазона, и если вы добавите 1 к каждому из них, вы получите верхний предел диапазона. Таким образом, у вас должен быть массив из 9 диапазонов, каждый с нижним пределом и верхним пределом геохеша (всего 18 геохешей). Эти геошашки все еще в том более низком разрешении, начиная с шага 2.
  7. Затем вы конвертируете все 18 этих геошей в любую битовую глубину / разрешение, в которой вы сохранили все свои геохеши в своей базе данных. Обычно вы делаете это, сдвигая биты на желаемую битовую глубину.
  8. Теперь вы можете выполнить запрос диапазона для точек в этих 9 диапазонах, и вы получите все точки примерно на расстоянии от вашей исходной точки. Там не будет никакого перекрытия, поэтому вам не нужно делать какие-либо пересечения, просто запросы чистого диапазона, очень быстро. (то есть в redis: ZRANGEBYSCORE zsetname lowerLimit upperLimit, по 9 диапазонам, созданным на этом шаге)

Вы можете дополнительно оптимизировать (по скорости) это:

  1. Взяв эти 9 диапазонов из шага 6 и выяснив, куда они ведут друг к другу. Обычно вы можете уменьшить 9 отдельных диапазонов примерно до 4 или 5 в зависимости от того, где находится ваша координата. Это может сократить время вашего запроса вдвое.
  2. После того, как у вас есть окончательные диапазоны, вы должны держать их для повторного использования. Вычисление этих диапазонов может занять большую часть времени обработки, поэтому, если ваша исходная координата не сильно меняется, но вам нужно повторить один и тот же запрос расстояния, вы должны держать его готовым, а не вычислять его каждый раз.
  3. Если вы используете redis, попробуйте объединить запросы в MULTI / EXEC, чтобы они передавали их по конвейеру для повышения производительности.
  4. ЛУЧШАЯ часть: вы можете распределить шаги 2-7 на клиентах вместо того, чтобы выполнять все эти вычисления в одном месте. Это значительно снижает нагрузку на процессор в ситуациях, когда приходят миллионы запросов.

Вы можете еще больше повысить точность, используя функцию расстояния по окружности / тип haversine для возвращаемых результатов, если вы сильно заботитесь о точности.

Вот аналогичный метод, использующий обычные геохеши base32 и SQL-запрос вместо redis: https://github.com/davetroy/geohash-js

Я не хочу подключать свои собственные вещи, но я написал модуль для nodejs & redis, который делает это действительно простым в реализации. Посмотрите код, если хотите: https://github.com/arjunmehta/node-georedis

Арджун Мехта
источник
Несколько последующих вопросов В - Как вы рассчитываете соседей? Целочисленное хеширование позволяет обрезать (например, base32 z-кривой не позволяет, например, (7 очень далеко от 8 в geohash base32). Как метод описан в geohash-js github.com/davetroy/geohash-js/blob/ аналогично master / matrix.txt ? Хотя этот алгоритм должен генерировать близкие географические точки, geohash-js выполняет O (1) расчет только соседних ячеек
Максим Векслер,
Вау, это было так полезно. Столько опыта в этом ответе. Довольно сложная задача
Симон
9

Вопрос может быть прочитан несколькими способами. Я понимаю, что это означает, что у вас есть большое количество точек, и вы намереваетесь проверять их многократно с произвольными точками, заданными в виде координатных пар, и хотите получить n ближайших к зонду точек с заранее установленным n. (В принципе, если n будет меняться, вы можете настроить структуру данных для каждого возможного n и выбрать ее за O (1) раз для каждого зонда: это может занять очень длительное время установки и потребовать много оперативной памяти, но мы сказано игнорировать такие проблемы.)

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

Используя векторную структуру данных для диаграммы Вороного, поиск точки в многоугольнике займет время O (log (n)). Для практических целей вы можете сделать это O (1) с чрезвычайно малым неявным коэффициентом, просто создав растровую версию диаграммы. Значения ячеек в растре являются либо (i) указателем на список из n ближайших точек, либо (ii) указанием на то, что эта ячейка расположена между двумя или более областями на диаграмме. Тест для произвольной точки в точке (x, y) становится:

Fetch the cell value for (x,y).
If the value is a list of points, return it.
Else apply a vector point-in-polygon algorithm to (x,y).

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

Whuber
источник
3

Я использую геохэш для именно этого. Причина в том, что мне нужно было осуществлять поиск по близости, используя информационную систему в стиле пирамиды ... где геохеши с точностью до 8-го уровня были "основой" и формировали новые итоги для гео-хешей с 7-й точностью ... и так далее, и так далее , Это были площадь, типы почвенного покрова и т. Д. Это был очень причудливый способ сделать что-то очень причудливое.

Таким образом, геохэш 8-го уровня будет содержать такую ​​информацию, как:

Тип: трава акров: 1.23

и 7-е, 6-е .. и т. д. будет содержать информацию, как:

Типы травы: 123 акра: 6502

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

Мне удалось написать несколько функций, чтобы найти самые большие геохеши, которые составляют мой текущий видовой экран, а затем использовать их, чтобы найти геохэш со второй по величине точностью в окне просмотра. Это можно легко распространить на запросы с индексированным диапазоном, где я запрашивал бы минимум 86ssaaaa и максимум 86sszzzz для любой точности, которую я хотел.

Я делаю это с помощью MongoDB.

whardier
источник
3

Обновление для 2018-х годов, и некоторые математические фонды или историческое происхождение Geohash:

  • Вдохновением для Геохаша было простое чередование двоичных цифр , возможно, оптимизация наивных алгоритмов, чередующих десятичные цифры, таких как C-квадраты .

  • бинарное чередование привело к стратегии индекса индекса Z-порядка, естественно, изобретатель Geohash не начал «искать лучшую фрактальную кривую» ... Но любопытно, что такая оптимизация дизайна, лучшая фрактальная кривая, возможна (!).

Использовать S2 Geometry Library

Геометрический подход S2 лучше, чем Geohash, потому что он использует сферическую топологию шара (куб), использует дополнительную проекцию (так что все ячейки имеют почти одинаковую форму и близкую область), и потому что индексирование с помощью кривой Гильберта лучше, чем Z- кривая порядка :

... мы можем добиться большего ... Прерывистость при переходе от верхнего правого к нижнему левому четырехугольнику приводит к тому, что нам приходится разделять некоторые диапазоны, которые мы могли бы сделать смежными. (...) мы можем полностью устранить любые разрывы (...)
blog.notdot.net/2009 по пространственной индексации с помощью квадри-деревьев и кривых Гильберта

Теперь это бесплатная и эффективная библиотека, см. Https://s2geometry.io.

PS: есть также (хорошие) неофициальные упрощенные версии, такие как NodeJSs2-geometry , и множество «игровых площадок», надстроек и демонстраций, как s2.sidewalklabs.com .

Питер Краусс
источник
2

Я бы порекомендовал использовать запрос GEORADIUS в Redis.

Передайте данные, защищенные наилучшим подходящим уровнем геохеша, с помощью вызова GEOADD.

Кроме того, посмотрите на это -> ProximityHash .

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

Эшвин Наир
источник