MongoDB 2.4 Introduces Geospatial Indexing and Search for GeoJSON Geometries Point, ...

:

In case you are unfamiliar with the geospatial stuff, have a look at this introduction to geospatial indexing and searching with MongoDB.

In version 2.4 MongoDB introduces support for a subset of GeoJSON geometries. These geometries can be used both as data and query expressions.

GeoJSON

GeoJSON is a specification for describing geometrical shapes with the help of the JSON (JavaScript Object Notation) format. The basic shapes are points, line strings, polygons and compositions of these shapes. Here are some examples:

// a Point object
{ type: "Point", coordinates: [0.5, 0.5] }
 
// a LineString object
{ type: "LineString", coordinates: [ [100.0, 0.0], [101.0, 1.0] ] }
 
// a simple Polygon object, a box
{ "type": "Polygon",
  "coordinates": [
    [ [0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], [0.0, 0.0] ]
    ]
}

// a Point object { type: "Point", coordinates: [0.5, 0.5] }// a LineString object { type: "LineString", coordinates: [ [100.0, 0.0], [101.0, 1.0] ] }// a simple Polygon object, a box { "type": "Polygon", "coordinates": [ [ [0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0], [0.0, 0.0] ] ] }

GeoJSON Support in MongoDB

Prior to version 2.4 geospatial data in MongoDB was solely points. You were able to index points denoted by a two-dimensional array ( [1,2] ) or an embedded document with two fields ( {x:1, y:2} ). By using GeoJSON geometries you …

  • are able to persist not only points, buth also line strings and polygons and index(!) these shapes.
  • work with a standard format, letting you exchange data with other systems more easily.

MongoDB supports these GeoJSON geometries for indexing and querying:

So let’s add some documents holding using all of these shapes:

> use geo
switched to db geo
> db.shapes.drop()
false
> s = db.shapes
geo.shapes
> s.insert( {_id: "P1", shape: {type: "Point", coordinates: [2,2] } } )
> s.insert( {_id: "P2", shape: {type: "Point", coordinates: [3,6] } } )
> s.insert( {_id: "Poly1", shape: {type: "Polygon", coordinates: [[ [3,1], [1,2], [5,6], [9,2], [4,3], [3,1] ]] } })
> s.insert( {_id: "LS1", shape: {type: "LineString", coordinates: [ [5,2], [7,3], [7,5], [9,4] ] } } )

> use geo switched to db geo > db.shapes.drop() false > s = db.shapes geo.shapes > s.insert( {_id: "P1", shape: {type: "Point", coordinates: [2,2] } } ) > s.insert( {_id: "P2", shape: {type: "Point", coordinates: [3,6] } } ) > s.insert( {_id: "Poly1", shape: {type: "Polygon", coordinates: [[ [3,1], [1,2], [5,6], [9,2], [4,3], [3,1] ]] } }) > s.insert( {_id: "LS1", shape: {type: "LineString", coordinates: [ [5,2], [7,3], [7,5], [9,4] ] } } )

If we “draw” that collection, it may look like this:

GeoJSON geometries

GeoJSON Indexing

Of course, we were able to store these document prior to version 2.4. The really cool thing is that we can index the field shape with a new index type 2dsphere:

> s.ensureIndex({shape: "2dsphere"})
> s.getIndexes()
[
        {
                "v" : 1,
                "key" : {
                        "_id" : 1
                },
                "ns" : "geo.shapes",
                "name" : "_id_"
        },
        {
                "v" : 1,
                "key" : {
                        "shape" : "2dsphere"
                },
                "ns" : "geo.shapes",
                "name" : "shape_2dsphere"
        }
]

> s.ensureIndex({shape: "2dsphere"}) > s.getIndexes() [ { "v" : 1, "key" : { "_id" : 1 }, "ns" : "geo.shapes", "name" : "_id_" }, { "v" : 1, "key" : { "shape" : "2dsphere" }, "ns" : "geo.shapes", "name" : "shape_2dsphere" } ]

GeoJSON Queries

To perform queries with GeoJSON geometries, you can use some new query operators $geoIntersects and $geometry. First of all, we define some query geometries:

> BOX = {type: "Polygon", coordinates: [[ [0,0], [3,0], [3,3], [0,3], [0,0] ]] }
> LINESTRING = {type: "LineString", coordinates: [[1,4], [8,4]]}

> BOX = {type: "Polygon", coordinates: [[ [0,0], [3,0], [3,3], [0,3], [0,0] ]] } > LINESTRING = {type: "LineString", coordinates: [[1,4], [8,4]]}

We add these to our diagramm, so we can check the results visually:

GeoJSON search geometries

> s.find( {shape: {$geoIntersects: {$geometry: BOX}}}, {_id:1})
{ "_id" : "Poly1" }
{ "_id" : "P1" }
 
> s.find( {shape: {$geoIntersects: {$geometry: LINESTRING}}}, {_id:1})
{ "_id" : "Poly1" }
{ "_id" : "LS1" }

> s.find( {shape: {$geoIntersects: {$geometry: BOX}}}, {_id:1}) { "_id" : "Poly1" } { "_id" : "P1" }> s.find( {shape: {$geoIntersects: {$geometry: LINESTRING}}}, {_id:1}) { "_id" : "Poly1" } { "_id" : "LS1" }

Bingo – exactly what we expected! The full source code of the examples can be found at github.

With these search features one can easily implement some new use cases like

  • find all regions along a given path
  • find all regions covering a given point
  • find intersecting regions etc.

These use cases are common use in geograhical information systems (GIS).

There are some more interesting properties of the new 2dsphere index. Have a look at the online documentation.

Disclaimer

No pixels were harmed creating the above diagrams. Everything was rendered with gnuplot 😉