DEV Community

loading...

An Immersive Guide to Geospatial MongoDB Data

Akash Shyam
UI/UX Designer | MERN Stack Develop | Freelancer
・4 min read

Dealing with coordinates can be a huge pain, so why not take the easy way? Instead of wrestling with coordinates in the frontend, push it away to the backend and let mongoDB do the work. Today, we'll look at dealing with geospatial data in mongoDB or mongoose. First on our list is GeoJSON.

GeoJSON

It has various data types to make dealing with coordinates easy. Keep in mind that longitude comes before latitude:

1) Point

The simplest of all types, it's basically a coordinate i.e. a longitude and a latitude:

An image of a point

// A point
{ type: "Point", coordinates: [ 40, 5 ] }
Enter fullscreen mode Exit fullscreen mode
2) LineString

It's an array of array of coordinates, which will be joined to form a line. It basically consists of an array of points.

An image of linestring

// A linestring
{ type: "LineString", coordinates: [ [ 40, 5 ], [ 41, 6 ] ] }
Enter fullscreen mode Exit fullscreen mode
3) Polygon

It's a polygon, that's it! squares, rectangles, hexagons, decagons, quadrilaterals so on. This must be a closed figure(the first point must be the same as the last point, I'm sounding like my maths teacher now 😅).

We draw the sides of the polygon using linestrings. Now, to illustrate this better, I'm going to draw a very very simple coordinate grid(don't worry, nothing complicated).

Screenshot 2021-05-07 at 12.40.50

(I apologize on behalf of my bad designing skills for that crude grid)

Now, let's draw a simple polygon:

Screenshot 2021-05-07 at 12.45.15

Here's how we can represent this in code:

{
  type : "Polygon",
  coordinates : [
     [ [ 1 , 2 ] , [ 4 , 4 ] , [ 6 , 3 ] , [ 5 , 1 ], [1, 2] ],
  ]
}
Enter fullscreen mode Exit fullscreen mode

There are a few to notice in this:

  • The nested arrays go 3 levels deep, I'll get back to this in a minute

  • In the diagram, there are only 4 points but we have written 5 points in our code. This is because the first point and last point overlap in the diagram so it's not visible. To make this a closed figure and a valid Polygon, we need to specify the last value the same as the first value to tell mongoDB that the figure has ended.

As I promised earlier, I'm going to explain the secret of the nested arrays:

  • The first array holds all the rings(I'll explain this in a second).

  • The second one holds each ring. Now, what are rings? It's basically a hole inside the polygon. So, the figure holds the area between the polygons. Here's a diagram:

Screenshot 2021-05-07 at 12.55.45

The white triangle inside is a ring. The figure contains the area inside the outer polygon but outside the white area( the area shaded in red is contained).

  • Finally, the 3rd array is our old friend, the array of coordinates.

The code for this polygon would look like this:

{
  type : "Polygon",
  coordinates : [
     [ [ 1 , 2 ] , [ 4 , 4 ] , [ 6 , 3 ] , [ 5 , 1 ], [1, 2] ],
     [ [ 3 , 2 ] , [ 5 , 3 ] , [ 4 , 2 ] , [ 3 , 2 ] ],
  ]
}
Enter fullscreen mode Exit fullscreen mode

That was a long (and hopefully not boring) maths lesson. Let's actually play around with these types and learn about more operators.

$geometry

It holds our geometrical data type (eg: Point, Polygon) and is used in tandem with other operators.

{
  $geometry: {
    type : "Polygon",
    coordinates : [
     [ [ 1 , 2 ] , [ 4 , 4 ] , [ 6 , 3 ] , [ 5 , 1 ], [1, 2] 
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

$geoIntersects

It checks if the coordinates in a document are intersecting(not necessarily completely inside) the provided geometrical type(It has to be a 2d type i.e. Polygon).


Places.find(
   {
     location: {
       $geoIntersects: {
            $geometry: {
              type : "Polygon",
            coordinates : [
               [ 
                  [ 1 , 2 ] , [ 4 , 4 ] , [ 6 , 3 ] , [ 5 , 1 ], [1, 2] 
               ]
            ]
         }
       }
     }
   }
)

Enter fullscreen mode Exit fullscreen mode

$geoWithin

Checks if the coordinates in a document are completely inside the provided Polygon(single/multi ringed).


Places.find(
   {
     location: {
       $geoWithin: {
            $geometry: {
              type : "Polygon",
            coordinates : [
               [ 
                  [ 1 , 2 ] , [ 4 , 4 ] , [ 6 , 3 ] , [ 5 , 1 ], [1, 2] 
               ]
            ]
         }
       }
     }
   }
)

Enter fullscreen mode Exit fullscreen mode

If you want to do the same thing, but in a circle(i.e. in a particular radius from the coordinates) we use $center. It takes in an array which has two elements. The first is the coordinates for the center and the second is the radius. The format is [[coordinateX, coordinateY], radius]

Places.find(
  {
    location: { $geoWithin: { $center: [ [-74, 40.74], 10 ] }  
  }
);
Enter fullscreen mode Exit fullscreen mode

$near

Fetches all documents which are have a certain distance from a specified coordinate which can be controlled using minimum and maximum lengths. Also, it will sort the documents from nearest to farthest.

Places.find(
   {
     location:
       { $near :
          {
            $geometry: {
              type: "Point",
              coordinates: [ -68, 35 ] 
            },
          }
       }
   }
)
Enter fullscreen mode Exit fullscreen mode

We can optionally specify a $minDistance and $maxDistance.

Places.find(
   {
     location:
       { $near :
          {
            $geometry: {
              type: "Point",
              coordinates: [ -68, 35 ],
              $minDistance: 500,
              $maxDistance: 2000
            },
          }
       }
   }
)
Enter fullscreen mode Exit fullscreen mode

Conclusion

You might be thinking, how is this useful? Well,
Suppose our user is looking for restaurants near him, we can provide all restaurants in a 10 mile radius.

Thanks for reading until here. If you liked the article and learn something today, don't forget to leave a like and follow me on dev.to!

Discussion (0)