Bagaimana cara efisien mencari semua tengara dalam rentang tengara tertentu?

14

Saya mencoba memulai dengan proyek pencarian geo yang akan menemukan semua landmark di 10 km / mil (tidak penting untuk cerita ini) dari landmark tertentu.

Jadi misalnya, katakanlah saya memiliki database 1.000.000 landmark. Untuk menemukan semua landmark dalam jarak 10 mil dari tengara dengan koordinat tertentu, saya harus menghitung jarak antara tengara dari pencarian saya dan 1.000.000 tengara.

Apakah ada cara yang lebih baik untuk melakukan itu?

Alternatif yang saya pikirkan adalah mengkategorikan landmark seperti negara, wilayah, kota, lingkungan, bisnis, sejarah, dll. Sedemikian rupa sehingga bisnis dapat menjadi bagian dari lingkungan atau kota. Kota adalah bagian dari suatu daerah, negara, dll. Ini dapat mempersempit daftar perhitungan, tetapi masih banyak pekerjaan yang harus dilakukan agar pencarian menjadi cepat dan akurat.

Bisakah Google Maps API membantu?

Dario Granich
sumber
5
Anda mungkin bisa menghilangkan banyak yang baik hanya dengan melakukan perhitungan jarak Manhattan cepat dan kemudian melakukan filter kedua setelah itu untuk mengecualikan landmark yang berada dalam 10 km persegi tetapi berada di luar radius 10 km.
Neil
3
Teknologi basis data apa yang Anda gunakan? Jawabannya bukan database agnostik.
jpmc26
1
@Neil Sebagai umpan kedua, Anda dapat menyertakan tengara apa pun yang membuat x dan y keduanya berada dalam jarak 7 km dari titik asal tanpa menghitung jarak yang sebenarnya.
JimmyJames

Jawaban:

10

Sejak SQL Server 2008, ada tipe data geografi yang menyimpan lokasi (pasangan lat / lon) dan membuatnya mudah bagi Anda untuk menulis kueri terkait lokasi.

Ada jawaban StackOverflow yang ada yang membahas ini secara mendalam.

Permintaan dasar untuk menemukan 7 item terdekat :

USE AdventureWorks2012  
GO  
DECLARE @g geography = 'POINT(-121.626 47.8315)';  
SELECT TOP(7) SpatialLocation.ToString(), City FROM Person.Address  
WHERE SpatialLocation.STDistance(@g) IS NOT NULL  
ORDER BY SpatialLocation.STDistance(@g);  

Permintaan dasar untuk menemukan semuanya dalam jarak 100 m (jawaban kedua untuk pertanyaan)

-- Get the center point
DECLARE @g geography
SELECT @g = geo FROM yourTable WHERE PointId = something

-- Get the results, radius 100m
SELECT * FROM yourTable WHERE @g.STDistance(geo) <= 100
Flater
sumber
11
@KonradRudolph: Seperti halnya untuk setiap kolom SQL yang digunakan untuk query pada tabel dengan jumlah baris besar. Anda benar, tetapi komentar itu akan berlaku untuk hampir semua kueri SQL yang diposting sebagai jawaban.
Flater
2
Di mana Anda membaca "MS SQL Server" dalam pertanyaan?
Doc Brown
3
@Flater Saya setuju bahwa biasanya akan jelas dan berlebihan tetapi kata-kata OP sepertinya menyarankan mereka tidak mengetahui mekanisme seperti itu.
Konrad Rudolph
2
@ jpmc26: Anda terkejut bahwa saya mencantumkan opsi yang valid dan tidak menyertakan beberapa opsi lain? Apa? Jika Anda merasa relevan untuk menambahkan PostGIS, tambahkan sendiri jawabannya (yang Anda lakukan) dan jangan resor untuk mengkritik orang lain karena tidak memiliki ide yang sama dengan Anda.
Flater
3
Jawaban Anda tampaknya bagi saya pada dasarnya hanyalah promosi penjualan MS SQL. Komentar Anda menunjukkan bahwa mereka memindahkan basis data ke sesuatu yang akan menelan biaya 10 ribu dolar tanpa benar-benar bertanya tentang apa yang hanya terjadi pada situasi mereka sehingga membuatnya tampak lebih buruk. Itu bahkan tidak menggambarkan bagaimana OP benar-benar dapat mengimplementasikan permintaan mereka atau mendiskusikan fakta bahwa melakukan dan meningkatkan indeks spasial yang digunakan tidak semudah dalam MS SQL seperti pada DB lainnya. Juga tidak membahas konsep-konsep yang mendasarinya. Ini jawaban yang buruk, terlepas dari apakah itu "valid." Itu sebabnya itu menggangguku.
jpmc26
29

Gunakan database dengan dukungan untuk kueri GIS (sistem informasi geografis) . Sebagian besar basis data mendukung ini secara langsung atau memiliki ekstensi, tetapi perinciannya akan khusus untuk basis data (dalam jawaban mereka , Flater menunjukkan sintaks untuk SQL server).

Jika Anda perlu mengimplementasikan kueri tersebut di dalam aplikasi Anda, Anda bisa menerapkan struktur data yang memungkinkan kueri spasial, misalnya Pohon kd . Ini seperti pohon pencarian biner, kecuali bahwa setiap tingkat partisi pohon pada dimensi koordinat yang berbeda. Ini memungkinkan Anda untuk membatasi pencarian ke kandidat yang layak yang lebih kecil. Secara efektif, Anda menerjemahkan pencarian Anda "radius 10 km" menjadi batas untuk setiap dimensi koordinat, dan kencangkan batas saat Anda berulang ke pohon.

amon
sumber
5
Ada juga pertukaran stack GIS
BlueRaja - Danny Pflughoeft
8
PostGIS adalah pilihan bebas utama. Ini mendukung banyak, lebih dari tipe dan fungsi SIG yang sangat mendasar dari SQL Server. Tetapi ini adalah fungsi dasar.
jpmc26
@amon Saya menemukan komentar jpmc26 sebagai tambahan yang baik, dan tidak sebanyak mengkritik contoh Anda. "Jika Anda ingin memulai dari awal, Anda tidak perlu membayar untuk DB berlisensi - sumber terbuka gratis ini juga akan melakukan trik dengan sangat baik".
mgarciaisaia
11

Ya, ada cara yang lebih baik. Anda perlu menggunakan indeks spasial . Indeks ini mengatur metadata tentang geometri untuk menyaring geometri jauh dengan sangat cepat, menghemat banyak siklus CPU dengan menghindari perhitungan yang Anda gambarkan. Anda tidak perlu repot menerapkannya sendiri karena semua basis data relasional utama menyediakan tipe geometri spasial dan indeks untuk digunakan.

Yang ingin Anda lihat adalah kueri "dalam jarak" (kueri untuk geometri dalam jarak tertentu dari beberapa geometri lain). Ini sangat standar dan sangat banyak masalah yang dipecahkan dan dimungkinkan di semua database di atas (dan dibangun menjadi beberapa):

  • PostGIS: ST_DWithin
  • SQL Server: STDistance(Tidak jelas bahwa penggunaan indeks pada versi geografi 3D dari fungsi ini didukung)
  • Oracle: SDO_WITHIN_DISTANCE(Ini tidak mengatakan secara eksplisit bahwa itu akan memicu penggunaan indeks. Saya akan memeriksa rencana permintaan. Anda mungkin perlu menerapkan suatu SDO_FILTERuntuk mendapatkannya untuk menggunakan indeks.)
  • MySQL: Masih mencari tahu ini.

Solusi untuk memicu penggunaan indeks

Dalam kasus terburuk di mana Anda memiliki masalah dalam mendapatkan sistem untuk menggunakan indeks spasial dengan pertanyaan ini, Anda bisa menambahkan filter tambahan. Anda akan membuat kotak pembatas persegi dengan sisi panjang 2 * (jarak pencarian) berpusat di titik pencarian Anda dan membandingkan kotak pembatas kotak geometri dengan yang sebelum memeriksa jarak yang sebenarnya. Itulah yang dilakukan PostGIS di ST_DWithinatas secara internal.


Jarak dalam GIS

Meskipun indeks spasial sangat fantastis dan benar-benar solusi tepat untuk masalah Anda, perhitungan jarak bisa menjadi rumit secara logis. Khususnya, Anda perlu khawatir tentang proyeksi apa (pada dasarnya semua parameter untuk sistem koordinat) data Anda disimpan. Sebagian besar proyeksi 2D (hal-hal selain sistem koordinat sudut seperti berbagai proyeksi lat / long) mendistorsi panjang secara signifikan. Sebagai contoh, proyeksi Web Mercator (yang digunakan oleh Google, Bing, dan setiap penyedia peta dasar utama lainnya) memperluas area dan jarak yang semakin jauh seiring lokasi semakin jauh dari garis khatulistiwa . Saya mungkin salah karena saya tidak dididik secara resmi dalam GIS, tetapi yang terbaik yang pernah saya lihat untuk proyeksi 2D adalah beberapa yang spesifik yang menjanjikan jarak yang benar dari suatusatu, titik konstan di seluruh dunia. (Tidak, tidak praktis menggunakan proyeksi berbeda untuk setiap permintaan; itu akan membuat indeks Anda tidak berguna.)

Intinya adalah Anda harus memastikan matematika Anda akurat. Cara paling sederhana untuk melakukannya dari perspektif pengembangan adalah dengan menggunakan proyeksi sudut (Ini sering disebut sebagai "geografis.") Dan fungsi yang mendukung melakukan matematika menggunakan model spheroid, tetapi perhitungan ini sedikit lebih mahal daripada rekan-rekan 2D. dan beberapa DB mungkin tidak mendukung pengindeksan mereka. Jika Anda bisa mendapatkan kinerja yang dapat diterima menggunakannya, mungkin itulah cara yang harus dilakukan. Pilihan umum lainnya adalah proyeksi regional (seperti zona UTM) yang mendapatkan jarak dan area yang cukup dekat untuk dikoreksi jika data Anda terbatas pada bagian tertentu dunia. Apa yang terbaik untuk aplikasi Anda akan tergantung pada kebutuhan spesifik Anda,

Ini berlaku bahkan jika Anda tidak menggunakan indeks spasial bawaan. Data Anda memiliki beberapa proyeksi terlepas dari teknologi atau teknik apa yang saat ini Anda gunakan atau gunakan di masa depan, dan saat ini sudah memengaruhi setiap pertanyaan dan perhitungan yang Anda buat.

jpmc26
sumber
3

Saya setuju bahwa jika mungkin menggunakan dukungan khusus dalam database akan menjadi cara yang paling masuk akal untuk melakukan ini.

Namun jika saya harus melakukan ini pada database tanpa dukungan spesifik, saya akan mulai dengan meminta kuadrat yang melingkupi misalnya lingkaran (y> (y1 - rad)) DAN (y <(y1 + rad)) DAN (x> ( x1 - rad)) AND (x <(x1 + rad)). Dengan asumsi poin Anda memiliki kueri distribusi yang hampir merata untuk sebuah bujur sangkar akan memberi Anda kecocokan sejati Anda plus sekitar 30% kecocokan salah tambahan. Anda kemudian dapat menyisihkan kecocokan palsu.

Peter Green
sumber
Tetapi tanpa indeks spasial yang tepat, permintaan semacam itu akan memindai paling buruk seluruh database, paling baik semua item dalam garis lintang ATAU garis bujur yang diberikan tergantung pada indeks Anda, yaitu "pita" dan bukan kuadrat. Jika Anda tidak ingin mematikan kinerja, gunakan database yang mendukung indeks spasial!
jcaron
@ jcaron Saya yakin permintaan ini dapat dioptimalkan dengan indeks B-tree biasa xdan y. (Mungkin digabungkan, mungkin terpisah. Saya akan sedikit profil untuk mencari tahu mana yang lebih baik dalam praktek.)
jpmc26
@ jpmc26 Tidak, tidak bisa. Pikirkan baik-baik, Anda akan lihat.
jcaron
@ jcaron Mungkin akan lebih baik jika Anda tidak samar tentang sesuatu yang jelas tidak mudah. B-tree dapat digunakan untuk BETWEENpermintaan. Saya tidak melihat mengapa kasus terburuk Anda tidak dapat memiliki 2 indeks dan kemudian hasil yang disaring dari setiap indeks bergabung bersama. (Itu adalah sesuatu yang dilakukan RDBMS secara internal ketika mereka anggap layak menggunakan beberapa indeks.) Jika indeks gabungan berfungsi, itu harus menyaring satu dimensi sepenuhnya pada tingkat pertama dan kemudian secara relatif cepat mempersempit di tingkat kedua.
jpmc26
2
@ jcaron sebenarnya Anda dapat menggunakan indeks untuk sesuatu seperti y between -68 and -69 and x between 10 and 11tetapi tentu saja indeks spasial melakukan pekerjaan yang lebih baik untuk tugas itu
Juan Carlos Oropeza