Використовуєте геохаш для пошуку близькості?


30

Я прагну оптимізувати географічний час близької точки пошуку.

Моє введення - Lat, lng point, і я шукаю за попередньо обчисленим набором локацій до n найближчих точок.

Мені все одно, скільки часу / місця займе будівництво попередньо обчисленого індексу місць, але мені все одно, що запити будуть надшвидкими.

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

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


Чи є значна різниця між використанням геогаша і зберіганням вашої лат / довгої на сходах / північ (наприклад)? Імовірно, що з обох ви можете змінити точність пошуку, обрізаючи символи / цифри. (Це суто питання з цікавості - мені ця тема незнайома).
djq

Чи зберігаються ці точки в базі даних або в пам'яті чи?
Марк Пфістер

@MarcPfister цій проблемі вже 2 роки (для мого використання), але вона завжди актуальна для громади, тому я продовжуватиму активне обговорення. Обговорені дані дійсно зберігалися в базі даних nosql.
Максим Векслер

Крім того, я вважаю, що з моменту відповіді на це питання MongoDB успішно здійснив індексацію та пошук геогашу, що підтверджує це. Я ще не бачив білого документа про реалізацію, але код відкритий і доступний будь-якій зацікавленій стороні.
Максим Векслер

Ну, тоді добре. CouchDB також мала просторову індексацію зараз, ймовірно, також використовуючи geohash.
Марк Пфістер

Відповіді:


25

Абсолютно можна. І це може бути досить швидко. (Інтенсивні обчислювальні біти ТАКОЖ можна розподілити)

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

Метод викладено тут: https://github.com/yinqiwen/ardb/wiki/Spatial-Index

Але суть у цьому (перефразовуючи посилання):

  1. Ви зберігаєте всі ваші геохімічні точки у найкращому дозволі (максимальне, як правило, 64-бітове ціле число, якщо це доступно, або у випадку javascript, 52 біт) в упорядкованому наборі (наприклад, zset in redis). У більшості бібліотек geohash в наші дні вбудовані цілочисельні функції geohash, і вам потрібно буде використовувати їх замість більш поширених базових32 геохашей.
  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 верхнійLimit, протягом 9 діапазонів, що створюються на цьому кроці)

Ви можете додатково оптимізувати (з урахуванням швидкості):

  1. Взяття цих 9 діапазонів від кроку 6 та визначення місця, де вони ведуть один до одного. Зазвичай ви можете зменшити 9 окремих діапазонів приблизно до 4 або 5 залежно від місця координат. Це може скоротити час запиту вдвічі.
  2. Після того, як у вас будуть остаточні діапазони, слід утримувати їх для повторного використання. Обчислення цих діапазонів може зайняти більшу частину часу обробки, тому, якщо ваша вихідна координата не сильно зміниться, але вам потрібно повторно виконати однаковий запит на відстань, слід тримати це готовим, а не обчислювати його щоразу.
  3. Якщо ви використовуєте redis, спробуйте об'єднати запити в MULTI / EXEC, щоб він забезпечив їх для кращої продуктивності.
  4. НАЙКРАЩА частина: Ви можете розподілити кроки 2-7 для клієнтів, а не робити обчислення в одному місці. Це значно зменшує завантаження процесора в ситуаціях, коли надходять мільйони запитів.

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

Ось подібна методика, що використовує звичайні базові32 геогеши і запит SQL замість redis: https://github.com/davetroy/geohash-js

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


Пару подальших запитань Q - Як ви підрахуєте сусідів? Чи є ціле хешування дозволяє обрізати (base32 z-крива на основі цього не робить, наприклад (7 дуже далеко від 8 в base32 geohash). Як спосіб описаний в 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) растрова сітка повинна бути достатньо тонкою, щоб порівняно мало точок зонду потрапляло в осередки, що обробляють кілька областей Вороного. Це завжди можна досягти, маючи великі витрати на зберігання сіток.


3

Я використовую геохаші саме для цього. Причина в тому, що мені потрібно було здійснити пошук близькості за допомогою інформаційної системи в стилі піраміди .. де геохаши з точністю 8 рівня були «базою» і утворювали нові підсумки для геохашей 7-ї точності .. і так далі і так далі . Ці підсумки були площею, типами ґрунтового покриву тощо. Це був дуже фантазійний спосіб зробити дуже вигадливі речі.

Тож геохаші 8-го рівня містили б інформацію:

тип: трава соток: 1,23

і 7-е, 6-е .. тощо. містили б інформацію:

трава_типи: 123 десятини: 6502

Це завжди будувалося з найменшої точності. Це дозволило мені дуже швидко робити всілякі статистичні дані. Я також зміг призначити посилання на геометрію для кожної посилання на Geohash за допомогою GeoJSON.

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

Я роблю це за допомогою MongoDB.


3

Оновлення на 2018 рік, а також деякі математичні фонди або історичні джерела Geohash:

  • натхнення для Geohash був простий interlave довічних цифр , можливо, оптимізація наївних алгоритмів, що чергуються десяткових цифр, як з C-квадратів .

  • бінарне переплетення призвело до стратегії індексу кривої Z-порядку, природно, винахідник Geohash не почав "шукати найкращу фрактальну криву" ... Але цікаво, що оптимізація дизайну, краща фрактальна крива, можлива (!).

Використовуйте бібліотеку геометрії S2

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

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

Зараз це безкоштовна та ефективна бібліотека, дивіться https://s2geometry.io

PS: Є також (хороші) неофіційні спрощені версії, як NodeJSs2-geometry , і багато "ігрових майданчиків", надбудов і демонстрацій, як s2.sidewalklabs.com .


2

Я б рекомендував використовувати запит GEORADIUS в редакторі redis.

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

Також подивіться на це -> ProximityHash .

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.