Have you ever heard of GeoJSON? If not, let me explain it to you.
GeoJSON is a format for encoding a variety of geographic data structures created and published in 2008.
Since 2015, it has an official specification created by its original authors with the collaboration of the Internet Engineering Task Force (IETF).
According to this specification:
GeoJSON is a geospatial data interchange format based on JavaScript Object Notation (JSON). It defines several types of JSON objects and the manner in which they are combined to represent data about geographic features, their properties, and their spatial extents. GeoJSON uses a geographic coordinate reference system, World Geodetic System 1984, and units of decimal degrees.
It is a format widely used across JSON-based applications to read, manipulate and compare geospatial data.
Several third-party map libraries provide GeoJSON support for front-end applications, some of them are:
Leaflet
Openlayers
Google Maps
Considering that now you know a little bit more about GeoJSON, let´s move to the manipulation bit.
When building maps for a web application, it is common to deal with something we call "Layers". According to Google:
Layers are objects on the map that consist of one or more separate items but are manipulated as a single unit. Layers generally reflect collections of objects that you add on top of the map to designate a common association.
A Layer can have different formats and one of them is GeoJSON.
So, when working with more than one layer in a map, it is common to perform operations between them to determine their geospatial relationship within a context. By making use of geospatial operations, it is possible to determine, for example, if:
i) a Point lies within a Polygon;
ii) a Polygon intersects another Polygon;
iii) a Line crosses a circle;
iv) a Point is part of a Line;
But, depending on the size of the involved layers, manipulating or comparing them directly with JavaScript, in the browser, might cause performance issues since the memory usage tends to increase even more if they need to be rendered on a map.
A good solution for this case is to perform any required geospatial operation directly in the database. Most of the database engines provide functions to query GeoJSON and one of them is MongoDB which, by the way, has been pretty optimized for spatial operations since version 3.2.
These are some of the most basic geospatial structures that MongoDB supports:
Point
{
"type": "Point",
"coordinates": [
-42.53700256347656,
-22.28496664336935
]
}
Linestring
{
"type": "LineString",
"coordinates": [
[
-43.17626953125,
-22.91792293614603
],
[
-40.2978515625,
-20.262197124246534
]
]
}
Polygon
{
"type": "Polygon",
"coordinates": [
[
[
-42.94006347656249,
-22.649502094242195
],
[
-42.16552734375,
-22.649502094242195
],
[
-42.16552734375,
-21.968519331082298
],
[
-42.94006347656249,
-21.968519331082298
],
[
-42.94006347656249,
-22.649502094242195
]
]
]
}
Geometry Collection
{
type: "GeometryCollection",
geometries: [
{
"type": "Point",
"coordinates": [
-42.53700256347656,
-22.28496664336935
]
},
{
"type": "LineString",
"coordinates": [
[
-43.17626953125,
-22.91792293614603
],
[
-40.2978515625,
-20.262197124246534
]
]
},
{
"type": "Polygon",
"coordinates": [
[
[
-42.94006347656249,
-22.649502094242195
],
[
-42.16552734375,
-22.649502094242195
],
[
-42.16552734375,
-21.968519331082298
],
[
-42.94006347656249,
-21.968519331082298
],
[
-42.94006347656249,
-22.649502094242195
]
]
]
}
]
}
Here you can find the mentioned examples plotted in a map using geojson.io.
Now, I'm going to show you some basic operations that can be performed directly in a Mongo database assuming that there is already a collection named geospatial
with the proper indexes set up.
To ease the query implementation, I recommend setting the GeoJSON object inside a property of your document. In the following example, the property "location" represents the geometry:
{
"$id": "342j3ug4kj23b4k234"
"location": {
"type": "Point",
"coordinates": [ -22, -42 ]
}
}
1. Finding geometries that intersect with the given Point
db.geospatial.find({
location: {
$geoIntersects: {
$geometry: {
type: "Point",
coordinates: [
-42.53700256347656,
-22.28496664336935
]
}
}
}
})
This query will retrieve all geometries with which the given Point intersects. This example shows a query passing a Point as a parameter, but it supports any GeoJSON Object
2. Finding all geometries that lie within a given geometry
db.geospatial.find({
location: {
$geoWithin: {
$geometry: {
"type": "Polygon",
"coordinates": [
[
[
-42.94006347656249,
-22.649502094242195
],
[
-42.16552734375,
-22.649502094242195
],
[
-42.16552734375,
-21.968519331082298
],
[
-42.94006347656249,
-21.968519331082298
],
[
-42.94006347656249,
-22.649502094242195
]
]
]
}
}
}
})
This query will retrieve all geometries that are inside the given Polygon and it is useful to look for all points that are placed within an area. For example: let's say you'd like to find all restaurants in a given city.
3. Finding geometries within a number of miles from a given point
db.geospatial.find({
location: {
$geoWithin: {
$centerSphere: [
[ -42.53700256347656, -22.28496664336935 ],
5 / 3963.2
]
}
}
})
Using $centerSphere
, MongoDB will "draw" a circle with a pre-defined size from a given point. In this case, the query will retrieve all geometries that are up to 5 miles to the coordinates provided.
It is important to mention that, in this case, the geometries will be retrieved unordered.
4. Finding ordered geometries within a number of miles from a given point
const METERS_PER_MILE = 1609.34
db.geospatial.find({
location: {
$nearSphere: {
$geometry: {
type: "Point",
coordinates: [ -73.93414657, 40.82302903 ]
},
$maxDistance: 5 * 1609.34
}
}
})
This query will retrieve the same result as the previous one, but order from the nearest to the farthest.
Check this page for more information on distance unit conversion.
GeoJSON is the most used format to represent Geospatial data in web applications. It is fully supported by the most popular third-party map rendering libraries and database engines.
This article demonstrated some basic concepts and how to implement some of the spatial operations provided by MongoDB.
I hope you've liked it.
Please, share and comment... any feedback is welcome!
Top comments (2)
Hello :)
Thank you for this post. I found it a pretty good/easy to follow introduction about spatial query in MongoDB.
Just wondering if you have done similar thing (spatial query) with other database? especially PostgreSQL(what I mainly use for spatial stuff) with its build in functions i.e. ST_intersects, ST_envelope. and if so, would you mind share some comment around comparison between both (pros & cons, limitation, performance etc)
Thanks a lot!
Hi @definitelydaphne,
thank you so much for interacting with the post.
I have never worked with PostgreSQL to deal with Geospatial data, even though I'm aware it is widely used around the world this purpose.
But I had the chance to work with MySQL 5.6/5.7 to manage layer and GeoJSON in a smart city project I've participated in.
I don't have any bookmarks stored from that time, but what I can tell you is that, back at that time, we chose to migrate our geospatial database to MongoDB and the results were incredible.
We had more than 50k features (geometries represented in GeoJSON) and we had to query them frequently.
The speed increased considerably when we moved to the NoSQL database.
Another thing I can mention is that, using MongoDB, at least the way we were implementing the project, made our efforts to build the queries smaller because instead of building SQL strings to perform complex queries, we took advantage of the object-formatted way of building queries using a lib for MongoDB.
By the way, it is important to say that we used PHP in that project.
I hope it helped you!