Geospatiale ondersteuning in MongoDB

1. Overzicht

In deze tutorial verkennen we de geospatiale ondersteuning in MongoDB.

We zullen bespreken hoe georuimtelijke gegevens, geo-indexering en georuimtelijke zoekopdrachten kunnen worden opgeslagen. We zullen ook meerdere georuimtelijke zoekopdrachten gebruiken, zoals in de buurt, geoWithin, en geoIntersects.

2. Geospatiale gegevens opslaan

Laten we eerst eens kijken hoe geospatiale gegevens in MongoDB kunnen worden opgeslagen.

MongoDB ondersteunt meerdere GeoJSON typen om georuimtelijke gegevens op te slaan. In onze voorbeelden gebruiken we voornamelijk de Punt en Veelhoek types.

2.1. Punt

Dit is de meest elementaire en meest voorkomende GeoJSON type, en het wordt gebruikt om een ​​specifiek punt op het raster weer te geven.

Hier hebben we een eenvoudig object, in onze plaatsen verzameling, dat heeft veld plaats als een Punt:

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

Merk op dat de lengtegraad eerst komt, dan de breedtegraad.

2.2. Veelhoek

Veelhoek is een beetje ingewikkelder GeoJSON type.

We kunnen gebruiken Veelhoek om een ​​gebied met zijn buitengrenzen te definiëren en indien nodig ook inwendige gaten.

Laten we een ander object bekijken waarvan de locatie is gedefinieerd als een Veelhoek:

{"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": "Polygoon"}}

In dit voorbeeld hebben we een reeks punten gedefinieerd die buitengrenzen vertegenwoordigen. We moeten ook de grens sluiten zodat het laatste punt gelijk is aan het eerste punt.

Merk op dat we de buitengrenzen tegen de klok in moeten definiëren en de gatgrenzen in de richting van de klok.

Naast deze soorten zijn er ook veel andere soorten zoals LineString, MultiPoint, Meerdere polygonen, MultiLineString, en GeometryCollection.

3. Geospatiale indexering

Om zoekopdrachten uit te voeren op de geospatiale gegevens die we hebben opgeslagen, moeten we een geospatiale index maken op onze plaats veld.

We hebben in principe twee opties: 2d en 2D-bol.

Maar laten we eerst onze plaatsen bepalen collection:

MongoClient mongoClient = nieuwe MongoClient (); MongoDatabase db = mongoClient.getDatabase ("myMongoDb"); collection = db.getCollection ("plaatsen");

3.1. 2d Geospatial Index

De 2d index stelt ons in staat om zoekopdrachten uit te voeren die werken op basis van 2D-vlakberekeningen.

We kunnen een 2d index op de plaats veld in onze Java-applicatie als volgt:

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

Natuurlijk kunnen we hetzelfde doen in de Mongo schelp:

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

3.2. 2D-bol Geospatial Index

De 2D-bol index ondersteunt zoekopdrachten die werken op basis van bolberekeningen.

Evenzo kunnen we een 2D-bol index in Java met dezelfde Indexen klasse zoals hierboven:

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

Of in de Mongo schelp:

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

4. Zoeken met behulp van georuimtelijke zoekopdrachten

Laten we nu voor het opwindende deel naar objecten zoeken op basis van hun locatie met behulp van georuimtelijke zoekopdrachten.

4.1. In de buurt van zoekopdracht

Laten we beginnen met in de buurt. We kunnen de in de buurt zoekopdracht om te zoeken naar plaatsen binnen een bepaalde afstand.

De in de buurt query werkt met beide 2d en 2D-bol indices.

In het volgende voorbeeld zoeken we naar plaatsen die minder dan 1 km en meer dan 10 meter verwijderd zijn van de opgegeven positie:

@Test openbare leegte gegevenNearbyLocation_whenSearchNearby_thenFound () {Punt currentLoc = nieuw punt (nieuwe positie (-0.126821, 51.495885)); FindIterable resultaat = collection.find (Filters.near ("location", currentLoc, 1000.0, 10.0)); assertNotNull (result.first ()); assertEquals ("Big Ben", result.first (). get ("naam")); }

En de bijbehorende vraag in het Mongo schelp:

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

Merk op dat de resultaten zijn gesorteerd van het dichtst naar het verst.

Evenzo, als we een zeer afgelegen locatie gebruiken, zullen we geen plaatsen in de buurt vinden:

@Test openbare leegte gegevenFarLocation_whenSearchNearby_thenNotFound () {Punt currentLoc = nieuw punt (nieuwe positie (-0.5243333, 51.4700223)); FindIterable resultaat = collection.find (Filters.near ("location", currentLoc, 5000.0, 10.0)); assertNull (result.first ()); }

We hebben ook de nearSphere methode, die precies zo werkt als in de buurt, behalve dat het de afstand berekent met behulp van sferische geometrie.

4.2. Binnen Query

Vervolgens verkennen we het geoWithin vraag.

De geoWithin query stelt ons in staat om te zoeken naar plaatsen die volledig bestaan ​​binnen een bepaald gegeven Geometrie, zoals een cirkel, kader of veelhoek. Dit werkt ook met beide 2d en 2D-bol indices.

In dit voorbeeld zoeken we naar plaatsen die binnen een straal van 5 km vanaf de opgegeven middenpositie bestaan:

@Test openbare leegte gegevenNearbyLocation_whenSearchWithinCircleSphere_thenFound () {dubbele afstandInRad = 5.0 / 6371; FindIterable resultaat = collection.find (Filters.geoWithinCenterSphere ("locatie", -0.1435083, 51.4990956, distanceInRad)); assertNotNull (result.first ()); assertEquals ("Big Ben", result.first (). get ("naam")); }

Merk op dat we de afstand van km naar radialen moeten transformeren (deel gewoon door de straal van de aarde).

En de resulterende vraag:

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

Vervolgens zoeken we naar alle plaatsen die binnen een rechthoekig "vak" bestaan. We moeten de box definiëren aan de hand van de positie linksonder en rechtsboven:

@Test openbare leegte gegevenNearbyLocation_whenSearchWithinBox_thenFound () {dubbele lowerLeftX = -0.1427638; dubbele lowerLeftY = 51.4991288; dubbele upperRightX = -0.1256209; dubbele upperRightY = 51.5030272; FindIterable resultaat = collection.find (Filters.geoWithinBox ("locatie", lowerLeftX, lowerLeftY, upperRightX, upperRightY)); assertNotNull (result.first ()); assertEquals ("Big Ben", result.first (). get ("naam")); }

Hier is de bijbehorende vraag in Mongo schelp:

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

Tenslotte, als het gebied waarin we willen zoeken geen rechthoek of cirkel is, kunnen we een polygoon gebruiken om een ​​specifieker gebied te definiëren:

@Test openbare leegte gegevenNearbyLocation_whenSearchWithinPolygon_thenFound () {ArrayList points = nieuwe 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 resultaat = collection.find (Filters.geoWithinPolygon ("locatie", punten)); assertNotNull (result.first ()); assertEquals ("Big Ben", result.first (). get ("naam")); }

En hier is de bijbehorende vraag:

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

We hebben alleen een polygoon gedefinieerd met zijn buitengrenzen, maar we kunnen er ook gaten aan toevoegen. Elke hole is een Lijst van Punts:

geoWithinPolygon ("locatie", punten, hole1, hole2, ...)

4.3. Intersect-zoekopdracht

Laten we tot slot eens kijken naar het geoIntersects vraag.

De geoIntersects query vindt objecten die op zijn minst een bepaalde Geometrie. Ter vergelijking, geoWithin vindt objecten die volledig bestaan ​​binnen een gegeven Geometrie.

Deze vraag werkt met de 2dsphere alleen index.

Laten we dit eens in de praktijk bekijken, met een voorbeeld van het zoeken naar een plaats die een Veelhoek:

@Test openbare leegte gegevenNearbyLocation_whenSearchUsingIntersect_thenFound () {ArrayList posities = nieuwe ArrayList (); posities.add (nieuwe positie (-0.1439, 51.4952)); posities.add (nieuwe positie (-0.1346, 51.4978)); posities.add (nieuwe positie (-0.2177, 51.5135)); posities.add (nieuwe positie (-0.1439, 51.4952)); Polygoongeometrie = nieuwe polygoon (posities); FindIterable resultaat = collection.find (Filters.geoIntersects ("locatie", geometrie)); assertNotNull (result.first ()); assertEquals ("Hyde Park", result.first (). get ("naam")); }

De resulterende vraag:

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. Conclusie

In dit artikel hebben we geleerd hoe we geospatiale gegevens kunnen opslaan in MongoDB en hebben we gekeken naar het verschil tussen 2d en 2D-bol geospatiale indices. We hebben ook geleerd hoe we in MongoDB kunnen zoeken met behulp van georuimtelijke zoekopdrachten.

Zoals gewoonlijk is de volledige broncode voor de voorbeelden beschikbaar op GitHub.