4 ways to query GeoJSON data in MongoDB
Tags: mongodb, geojson, development, maps
Published: 2021-04-13 21:22:17

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](https://tools.ietf.org/html/rfc7946) created by its original authors with the collaboration of the Internet Engineering Task Force [(IETF)](https://www.ietf.org/).

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](https://leafletjs.com/reference-1.7.1.html#geojson)
[Openlayers](https://openlayers.org/en/latest/examples/geojson.html)
[Google Maps](https://developers.google.com/maps/documentation/javascript/datalayer#load_geojson)

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](https://developers.google.com/maps/documentation/javascript/layers):

> 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](https://docs.mongodb.com/manual/reference/geojson/) which, by the way, has been pretty optimized for spatial operations since [version 3.2](https://www.mongodb.com/blog/post/geospatial-performance-improvements-in-mongodb-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](http://geojson.io/#id=gist:vcpablo/2758df2cb48a5b1031b77a18c60cea9d&map=8/-21.596/-41.737) 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](https://docs.mongodb.com/manual/core/2dsphere/) 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](https://docs.mongodb.com/manual/reference/geojson/)

## 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](https://docs.mongodb.com/manual/tutorial/calculate-distances-using-spherical-geometry-with-2d-geospatial-indexes/) 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!