Dukungan Geospasial di MongoDB

1. Ikhtisar

Dalam tutorial ini, kita akan menjelajahi dukungan Geospasial di MongoDB.

Kami akan membahas cara menyimpan data geospasial, pengindeksan geo, dan pencarian geospasial. Kami juga akan menggunakan beberapa kueri penelusuran geospasial seperti near , geoWithin , dan geoIntersects .

2. Menyimpan Data Geospasial

Pertama, mari kita lihat cara menyimpan data geospasial di MongoDB.

MongoDB mendukung beberapa tipe GeoJSON untuk menyimpan data geospasial. Di seluruh contoh kami, kami terutama akan menggunakan jenis Titik dan Poligon .

2.1. Titik

Ini adalah tipe GeoJSON yang paling dasar dan umum , dan digunakan untuk mewakili satu titik tertentu di grid .

Di sini, kami memiliki objek sederhana, di koleksi tempat kami , yang memiliki lokasi bidang sebagai Titik :

{ "name": "Big Ben", "location": { "coordinates": [-0.1268194, 51.5007292], "type": "Point" } }

Perhatikan bahwa nilai garis bujur diutamakan, kemudian garis lintang.

2.2. Poligon

Poligon adalah jenis GeoJSON yang sedikit lebih kompleks .

Kita bisa menggunakan Polygon untuk menentukan area dengan batas luarnya dan juga lubang interiornya jika diperlukan.

Mari kita lihat objek lain yang lokasinya ditetapkan sebagai Poligon :

{ "name": "Hyde Park", "location": { "coordinates": [ [ [-0.159381, 51.513126], [-0.189615, 51.509928], [-0.187373, 51.502442], [-0.153019, 51.503464], [-0.159381, 51.513126] ] ], "type": "Polygon" } }

Dalam contoh ini, kami mendefinisikan larik titik yang mewakili batas luar. Kita juga harus menutup batasnya sehingga titik terakhir sama dengan titik pertama.

Perhatikan bahwa kita perlu menentukan titik batas luar dalam arah berlawanan arah jarum jam dan batas lubang searah jarum jam.

Selain tipe-tipe tersebut, ada juga banyak tipe lainnya seperti LineString, MultiPoint, MultiPolygon, MultiLineString, dan GeometryCollection.

3. Pengindeksan Geospasial

Untuk melakukan kueri penelusuran pada data geospasial yang kami simpan, kami perlu membuat indeks geospasial di bidang lokasi kami .

Kami pada dasarnya memiliki dua opsi: 2d dan 2dsphere .

Tapi pertama-tama, mari kita mendefinisikan tempat kami c ollection :

MongoClient mongoClient = new MongoClient(); MongoDatabase db = mongoClient.getDatabase("myMongoDb"); collection = db.getCollection("places");

3.1. Indeks Geospasial 2d

The 2d Indeks memungkinkan kita untuk melakukan query pencarian yang bekerja berdasarkan perhitungan pesawat 2d.

Kita dapat membuat indeks 2d pada bidang lokasi di aplikasi Java kita sebagai berikut:

collection.createIndex(Indexes.geo2d("location"));

Tentu saja, kita bisa melakukan hal yang sama di cangkang mongo :

db.places.createIndex({location:"2d"})

3.2. Indeks Geospasial 2dsphere

The 2dsphere Indeks mendukung query yang bekerja berdasarkan perhitungan bola.

Demikian pula, kita dapat membuat indeks 2dsphere di Java menggunakan kelas Indeks yang sama seperti di atas:

collection.createIndex(Indexes.geo2dsphere("location"));

Atau di dalam cangkang mongo :

db.places.createIndex({location:"2dsphere"})

4. Searching Using Geospatial Queries

Now, for the exciting part, let's search for objects based on their location using geospatial queries.

4.1. Near Query

Let's start with near. We can use the near query to search for places within a given distance.

The near query works with both 2d and 2dsphere indices.

In the next example, we'll search for places that are less than 1 km and more than 10 meters away from the given position:

@Test public void givenNearbyLocation_whenSearchNearby_thenFound() { Point currentLoc = new Point(new Position(-0.126821, 51.495885)); FindIterable result = collection.find( Filters.near("location", currentLoc, 1000.0, 10.0)); assertNotNull(result.first()); assertEquals("Big Ben", result.first().get("name")); }

And the corresponding query in the mongo shell:

db.places.find({ location: { $near: { $geometry: { type: "Point", coordinates: [-0.126821, 51.495885] }, $maxDistance: 1000, $minDistance: 10 } } })

Note that the results are sorted from nearest to farthest.

Similarly, if we use a very far away location, we won't find any nearby places:

@Test public void givenFarLocation_whenSearchNearby_thenNotFound() { Point currentLoc = new Point(new Position(-0.5243333, 51.4700223)); FindIterable result = collection.find( Filters.near("location", currentLoc, 5000.0, 10.0)); assertNull(result.first()); }

We also have the nearSphere method, which acts exactly like near, except it calculates the distance using spherical geometry.

4.2. Within Query

Next, we'll explore the geoWithin query.

The geoWithin query enables us to search for places that fully exist within a given Geometry, like a circle, box, or polygon. This also works with both 2d and 2dsphere indices.

In this example, we're looking for places that exist within a 5 km radius from the given center position:

@Test public void givenNearbyLocation_whenSearchWithinCircleSphere_thenFound() { double distanceInRad = 5.0 / 6371; FindIterable result = collection.find( Filters.geoWithinCenterSphere("location", -0.1435083, 51.4990956, distanceInRad)); assertNotNull(result.first()); assertEquals("Big Ben", result.first().get("name")); }

Note that we need to transform the distance from km to radian (just divide by Earth's radius).

And the resulting query:

db.places.find({ location: { $geoWithin: { $centerSphere: [ [-0.1435083, 51.4990956], 0.0007848061528802386 ] } } })

Next, we'll search for all places that exist within a rectangle “box”. We need to define the box by its lower left position and upper right position:

@Test public void givenNearbyLocation_whenSearchWithinBox_thenFound() { double lowerLeftX = -0.1427638; double lowerLeftY = 51.4991288; double upperRightX = -0.1256209; double upperRightY = 51.5030272; FindIterable result = collection.find( Filters.geoWithinBox("location", lowerLeftX, lowerLeftY, upperRightX, upperRightY)); assertNotNull(result.first()); assertEquals("Big Ben", result.first().get("name")); }

Here's the corresponding query in mongo shell:

db.places.find({ location: { $geoWithin: { $box: [ [-0.1427638, 51.4991288], [-0.1256209, 51.5030272] ] } } })

Finally, if the area we want to search within isn't a rectangle or a circle, we can use a polygon to define a more specific area:

@Test public void givenNearbyLocation_whenSearchWithinPolygon_thenFound() { ArrayList
    
      points = new ArrayList
     
      (); points.add(Arrays.asList(-0.1439, 51.4952)); points.add(Arrays.asList(-0.1121, 51.4989)); points.add(Arrays.asList(-0.13, 51.5163)); points.add(Arrays.asList(-0.1439, 51.4952)); FindIterable result = collection.find( Filters.geoWithinPolygon("location", points)); assertNotNull(result.first()); assertEquals("Big Ben", result.first().get("name")); }
     
    

And here's the corresponding query:

db.places.find({ location: { $geoWithin: { $polygon: [ [-0.1439, 51.4952], [-0.1121, 51.4989], [-0.13, 51.5163], [-0.1439, 51.4952] ] } } })

We only defined a polygon with its exterior bounds, but we can also add holes to it. Each hole will be a List of Points:

geoWithinPolygon("location", points, hole1, hole2, ...)

4.3. Intersect Query

Finally, let's look at the geoIntersects query.

The geoIntersects permintaan temuan benda yang paling berpotongan dengan diberikan Geometri. Sebagai perbandingan, geoWithin menemukan objek yang sepenuhnya ada dalam Geometri tertentu .

Kueri ini hanya berfungsi dengan indeks 2dsphere .

Mari kita lihat ini dalam praktiknya, dengan contoh mencari tempat apa pun yang berpotongan dengan Poligon :

@Test public void givenNearbyLocation_whenSearchUsingIntersect_thenFound() { ArrayList positions = new ArrayList(); positions.add(new Position(-0.1439, 51.4952)); positions.add(new Position(-0.1346, 51.4978)); positions.add(new Position(-0.2177, 51.5135)); positions.add(new Position(-0.1439, 51.4952)); Polygon geometry = new Polygon(positions); FindIterable result = collection.find( Filters.geoIntersects("location", geometry)); assertNotNull(result.first()); assertEquals("Hyde Park", result.first().get("name")); }

Kueri yang dihasilkan:

db.places.find({ location:{ $geoIntersects:{ $geometry:{ type:"Polygon", coordinates:[ [ [-0.1439, 51.4952], [-0.1346, 51.4978], [-0.2177, 51.5135], [-0.1439, 51.4952] ] ] } } } })

5. Kesimpulan

Pada artikel ini, kami mempelajari cara menyimpan data geospasial di MongoDB dan melihat perbedaan antara indeks geospasial 2d dan 2dsphere . Kami juga belajar cara mencari di MongoDB menggunakan kueri geospasial.

Seperti biasa, kode sumber lengkap untuk contoh tersedia di GitHub.