Yakınlık aramaları için geohash mı kullanıyorsunuz?


30

Nokta yakınlığı coğrafi arama süresini optimize etmek istiyorum.

Girişim lat, lng noktası ve en yakın noktalara n önceden hesaplanmış bir konum kümesi arıyorum.

Önceden hesaplanan konumlar dizininin oluşturulmasının ne kadar zaman / alan alacağı umurumda değil, ancak sorguların çok hızlı olmasını önemsiyorum.

Arama anahtarı olarak geohash'ı kullanmayı düşünüyorum, ilk önce anahtarın X karakterleri için sonuçları alıp almadığımı kontrol edeceğim ve sonra sonuçları görmeye başlayana kadar karakterleri kesmeye devam edeceğim.

Benim için (şu ana kadar çok seyrek) coğrafi endeks tekniklerini anlamam, bu yaklaşımın bilinen tüm diğer uygulamalara (R Tree ve co.) Kıyasla en hızlı sonuçları (sorgu süresi açısından) üretebilmesi gerekir.


Geohash kullanmak ile enlem / boyunu doğuda / kuzeydoğuda (örneğin) saklamak arasında önemli bir fark var mı? Muhtemelen her ikisiyle de, karakter / rakamları kırparak arama hassasiyetinizi değiştirebilirsiniz. (Bu tamamen meraktan bir soru - bu konuya aşina değilim).
djq

Bu noktalar bir veritabanında mı yoksa bellekte mi saklanıyor?
Marc Pfister

@MarcPfister bu konu 2 yaşında (kullanım davam için) ancak toplum için her zaman alakalı, bu yüzden aktif tartışmaya devam edeceğim. Tartışılan veriler gerçekten bir nosql veritabanında saklandı.
Maxim Veksler

Ayrıca, bu sorunun yanıtlanmasından itibaren MongoDB'nin bu noktayı ispatlayan geohash endeksleme ve aramayı başarıyla uyguladığına inanıyorum. Uygulamanın henüz bir tanıtım belgesini henüz görmedim ancak bu kod açık ve ilgilenen herhangi bir tarafın kullanımına açık.
Maxim Veksler

Ah tamam. CouchDB ayrıca muhtemelen geohash kullanarak da uzamsal endekslemeye sahipti.
Marc Pfister

Yanıtlar:


25

Kesinlikle yapabilirsin. Ve oldukça hızlı olabilir. (Yoğun hesaplama bitleri ayrıca ALSO dağıtılabilir)

Birkaç yol var, ancak birlikte çalıştığım bir yöntem tamsayı tabanlı coğrafyaların düzenli bir listesini kullanmak ve belirli bir coğrafi çözünürlük için en yakın tüm komşu coğrafya aralıklarını bulmak (ve kararınız distancekriterlerinize yaklaşıyor ). Bu coğrafi arama aralıklarını sorgulayarak yakındaki noktaların bir listesini almak için. Bunun için redis ve nodejs (örn. Javascript) kullanıyorum. Redis süper hızlıdır ve sipariş edilen aralıkları çok hızlı bir şekilde alabilir, ancak SQL veritabanlarının yapabildiği indeksleme sorgu manipülasyonu işlemlerinin çoğunu yapamaz.

Yöntem burada özetlenmiştir: https://github.com/yinqiwen/ardb/wiki/Spatial-Index

Ancak bunun özü şudur: (bağlantıyı parolalamak için):

  1. Tüm coğrafi noktalarınızı istediğiniz en iyi çözünürlükte saklarsınız (erişilebiliyorsa maksimum 64 bit tam sayı veya javascript durumunda, 52 bit) sipariş edilen bir sette (yani, redis cinsinden zset) saklarsınız. Günümüzde geohash kütüphanelerinin çoğunda yerleşik geohash tamsayı işlevleri bulunur ve bunları daha yaygın base32 geohash'leri yerine kullanmanız gerekir.
  2. İçinde aramak istediğiniz yarıçapı temel alarak arama alanınıza uygun bir bit derinliği / çözünürlüğü bulmanız gerekir ve bu, depolanmış coğrafi bit derinliğinizden daha az veya ona eşit olmalıdır. Bağlantılı site bir geohash'ın bit derinliğini sınırlayıcı kutu alanına metre cinsinden ilişkilendiren bir tabloya sahiptir.
  3. Ardından, orijinal koordinatınızı bu düşük çözünürlükte yeniden düzenlersiniz.
  4. Bu düşük çözünürlükte ayrıca 8 komşu (n, ne, e, se, s, sw, w, nw) coğrafi alanları da bulur. Komşu yöntemi yapmak zorunda olmanızın nedeni, birbirinin hemen yanında bulunan iki koordinatın tamamen farklı coğrafyalara sahip olabileceğinden, aramanın kapsadığı alanın bir ortalamasını yapmalısınız.
  5. Tüm komşu geohash'ları bu düşük çözünürlükte aldığınızda, koordinatınızın geohash'ını 3. adımdan itibaren listeye ekleyin.
  6. Ardından , bu 9 alanı kapsayacak şekilde aramak için bir coğrafi değer aralığı oluşturmalısınız . 5. adımdaki değerler sizin alt sınır limitinizdir ve her birine 1 eklerseniz, üst sınır limitinizi alırsınız. Öyleyse, her biri alt limiti ve üst geohash limitini (toplamda 18 geohash) içeren 9 aralığından oluşan bir diziniz olmalıdır. Bu geohashes hala 2. adımdan bu düşük çözünürlükte.
  7. Daha sonra, bu geohash'ların 18'ini, veritabanınızdaki tüm geohash'larınızı depoladığınız bit derinliği / çözünürlüğüne ne olursa olsun dönüştürürsünüz. Genellikle bunu, istenen bit derinliğine kadar kaydırarak yaparsınız.
  8. Şimdi bu 9 aralıktaki noktalar için bir aralık sorgusu yapabilir ve yaklaşık olarak orijinal noktanızın mesafesindeki tüm noktaları elde edersiniz. Örtüşme olmaz, bu yüzden herhangi bir kavşak yapmanıza gerek kalmaz, sadece saf aralık sorguları, çok hızlı. (yani, redis cinsinden: ZRANGEBYSCORE zsetname lowerLimit upperLimit, bu adımda üretilen 9 aralığın üzerinde)

Bunu şu şekilde daha da optimize edebilirsiniz (hız açısından):

  1. Bu 9'u adım 6'dan itibaren almak ve birbirlerine nereye gittiklerini bulmak. Genellikle, koordinatınızın bulunduğu yere bağlı olarak 9 ayrı aralığı yaklaşık 4 veya 5'e düşürebilirsiniz. Bu, sorgu sürenizi yarı yarıya azaltabilir.
  2. Nihai aralığınızı elde ettikten sonra, onları tekrar kullanmak üzere tutmalısınız. Bu aralıkların hesaplanması işlem süresinin çoğunu alabilir, bu nedenle orijinal koordinatınız çok değişmezse ancak aynı mesafe sorgusunu tekrar yapmanız gerekiyorsa, bunu her zaman hesaplamak yerine hazır tutmalısınız.
  3. Redis kullanıyorsanız, sorguları bir MULTI / EXEC içinde bir araya getirmeye çalışın, böylece biraz daha iyi performans için boruları birleştirin.
  4. EN İYİ kısım: Bu hesaplamayı tek bir yerde yapmak yerine 2-7 arasındaki adımları müşterilere dağıtabilirsiniz. Bu, milyonlarca isteğin geleceği durumlarda CPU yükünü büyük ölçüde azaltır.

Hassasiyeti çok önemsiyorsanız, döndürülen sonuçlarda daire mesafesi / haversine tipi işlevini kullanarak doğruluğu daha da artırabilirsiniz.

Burada sıradan base32 geohashes ve redis yerine SQL sorgusu kullanan benzer bir teknik var: https://github.com/davetroy/geohash-js

Kendi işimi bitirmek istemem ama nodejs & redis için bunu gerçekten kolaylaştıran bir modül yazdım. İsterseniz koda bir göz atın: https://github.com/arjunmehta/node-georedis


Bir çift takip S - Komşuları nasıl hesaplarsın? Tam sayı karma kesme sağlar mi (base32 z eğrisi göre Formül için değildir. (7) base32 geohash çok uzak 8 den. Yöntem de tarif edilir nasıl geohash-js github.com/davetroy/geohash-js/blob/ master / matrix.txt benzer? Bu algoritmanın geogash-js yakınlık coğrafi noktaları üretmesi beklenirken, O (1) sadece komşu hücrelerin hesaplamasını yapar
Maxim Veksler

Vay, bu çok faydalı oldu. Bu cevapta çok fazla uzmanlık var. Oldukça zorlu görev
simon

9

Bu soru birkaç şekilde okunabilir. Çok sayıda puanınız olduğu ve bunları defalarca koordinat çifti olarak verilen rasgele puanlarla sorgulama niyetinde olduğunuzu ve önceden sabit olarak n ile tespit edildiğinde sondaya en yakın noktaları elde etmek istediğinizi söylüyorum. (Prensip olarak, eğer n değişecek olursa, mümkün olan her n için bir veri yapısı oluşturabilir ve her prob için O (1) zaman içinde seçebilirsiniz: bu çok uzun bir kurulum süresi alabilir ve çok fazla RAM gerektirir, ancak biz Bu tür endişeleri görmezden gelmeleri söylenir.)

Yapı düzeni-n Voronoi diyagram tüm noktaları. Bu, düzlemi, her biri aynı komşu olan bağlı bölgelere ayırır. Bu durum, birçok verimli çözüme sahip olan poligon noktası problemine durumu azaltır.

Voronoi diyagramı için bir vektör veri yapısı kullanarak, poligondaki nokta aramaları O (log (n)) zaman alacaktır. Pratik amaçlar için, bu O (1) basitçe diyagramın raster versiyonunu oluşturarak son derece küçük bir katsayılı katsayı ile yapabilirsiniz. Rasterdeki hücrelerin değerleri, (i) en yakın n noktalarının bir listesine bir gösterici veya (ii) bu hücrenin diyagramda iki veya daha fazla bölgeye oturduğunun bir göstergesidir. (X, y) konumundaki rastgele bir nokta için test:

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) performansını elde etmek için, raster ağ gözü, çok az Voronoi bölgesini barındıran hücrelere nispeten az sayıda prob noktasının düşeceği kadar iyi olmalıdır. Bu her zaman, şebekeler için depolama potansiyel olarak büyük bir masrafla gerçekleştirilebilir.


3

Tam olarak bunun için geohashes kullanıyorum. Sebebim, piramit tarzı bilgi sistemi kullanarak yakınlık araştırmaları yapmamın gerekmesiydi. 8. seviye hassasiyetli geohashes 'üs' idi ve 7. hassasiyetin geohashleri ​​için yeni toplamlar oluşturdu .. . Bu toplamlar alan, toprak örtüsü çeşitleri vb. İdi. Bazı çok süslü şeyler yapmak için çok süslü bir yoluydu.

Yani 8. seviye geohashes gibi bilgiler içerecektir:

tür: çim dönüm: 1.23

ve 7., 6. .. vb. gibi bilgileri içerir:

grass_types: 123 dönüm: 6502

Bu her zaman en düşük hassasiyetten yapılmıştır. Bu, her türlü eğlenceli istatistiği çok hızlı bir şekilde yapmamı sağladı. GeoJSON kullanarak her geohash referansına bir geometri referansı atamayı da başardım.

Şu andaki görünüm alanımı oluşturan en büyük geohash'ları bulmak için çeşitli fonksiyonlar yazabildim ve daha sonra görünüm alanındaki ikinci en büyük hassasiyetin geohash'lerini bulmak için bunları kullandım. Bu, istediğim hassasiyet için minimum '86ssaaaa' ve maksimum '86sszzzz' sorgulayacağım endekslenmiş aralık sorgularına kolayca genişletilebilir.

Bunu MongoDB kullanarak yapıyorum.


3

2018'ler ve bazı matematik temelleri veya Geohash'ın tarihi kanıtı için güncelleme:

  • Geohash için ilham kaynağı oldu ikili basamak basit interlave belki gibi ondalık basamak serpiştirmeli naif algoritmaların bir optimizasyon ait C-kareler .

  • İkili bir araya geçme bir sonuçlandı Z-sırası-eğrisi doğal olarak Geohash mucit endeksi stratejisi değil "en iyi fraktal eğri arayan" başladı ... Ama curiosally bu tasarım optimizasyonu, daha iyi bir fraktal eğri mümkündür (!).

S2 Geometri Kütüphanesini Kullan

S2-geometri yaklaşımı daha iyidir çünkü Geohash, dünyanın küresel topolojisini (bir küp) kullanır, isteğe bağlı projeksiyon kullanır (böylece tüm hücrelerin aynı şekle ve yakın alana sahip olması gerekir) ve Hilbert-eğrisi ile indekslemenin daha iyi olması Z- sipariş eğrisi :

... daha iyisini yapabiliriz ... Yukarıdan aşağıya doğru sol dörtlüye geçerken süreksizlik, aksi halde bitişik yapabileceğimiz bazı aralıkları bölmemize neden olur. (...) Quadtrees ve Hilbert Curves ile Spatial indeksleme konusundaki herhangi bir kesintiyi tamamen ortadan kaldırabiliriz (...)
blog.notdot.net/2009

Şimdi ücretsiz ve verimli bir kütüphanedir, bkz. Https://s2geometry.io

Not: Ayrıca, NodeJSs2-geometry olarak (iyi) resmi olmayan basitleştirilmiş versiyonlar ve s2.sidewalklabs.com olarak birçok "oyun alanı", eklenti ve demo vardır .


2

GEORADIUS sorgusunu redis olarak kullanmanızı tavsiye ederim.

GEOADD çağrısını kullanarak en uygun geohash seviyesi ile paylaşılan veriye basın.

Ayrıca, şuna bir bak -> ProximityHash .

ProximityHash, merkez koordinatları ve yarıçapı göz önüne alındığında dairesel bir alanı kaplayan bir dizi coğrafya oluşturur. Ayrıca, en yüksek seviyeden başlayarak ve en iyi karışım demlenene kadar yineleyerek daireyi temsil etmek için çeşitli seviyelerde geohashes'in en iyi kombinasyonunu oluşturan GeoRaptor'ı kullanmak için ek bir seçeneğe sahiptir. Sonuç doğruluğu, başlangıç ​​geohash seviyesininkiyle aynı kalır, ancak veri boyutu önemli ölçüde azalır, böylece hız ve performans artar.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.