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

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] ]
    ]
}

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:

  • Point
  • LineString
  • Polygon

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] ] } } )

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"
        }
]

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]]}

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" }

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 😉

  • Facebook
  • Delicious
  • Digg
  • StumbleUpon
  • Reddit
  • Blogger
  • LinkedIn
Tobias Trelle

11 Responses to MongoDB 2.4 Introduces Geospatial Indexing and Search for GeoJSON Geometries Point, LineString and Polygon

  1. Robin Wagner says:

    Hi Tobias,
    that all works great. But I’m missing the possibility to store 3rd dimension (altitude) with the coordinates, corresponding to the GeoJSON-Spec.

    A position is represented by an array of numbers. There must be at least two elements, and may be more. The order of elements must follow x, y, z order (easting, northing, altitude for coordinates in a projected coordinate reference system, or longitude, latitude, altitude for coordinates in a geographic coordinate reference system). Any number of additional elements are allowed — interpretation and meaning of additional elements is beyond the scope of this specification.

    Regards, Robin

    • Hi Robin,

      AFAIK, MongoDB does not suppport indexes and queries for 3D coordinates: http://docs.mongodb.org/manual/applications/geospatial-indexes/

      You can have 3 (or more) values in your coordinate array like [1,2,3], but MongoDB honours only the first two of them.

      Best wishes
      Tobias

      • Robin Wagner says:

        Hi Tobias,
        of course ‘2dsphere’ index only works with 2 dimensions.
        But did you try [1,2,3] on an existing 2dsphere index ?

        > db.loc.ensureIndex({loc:'2dsphere'})
        > db.loc.save({loc:{type:'Point',coordinates:[1,2,3]}})
        Can't extract geo keys from object, malformed geometry?:{ type: "Point", coordinates: [ 1.0, 2.0, 3.0 ] }

        Have a nice weekend,
        Robin

        • That behaiviour (checking for exactly 2 coordinate values) is new in version 2.4. Prior to that, the “simple” 2d indexes ignored a third dimension:

          > db.loc.ensureIndex({loc:’2d’})
          > db.loc.save({loc: [1,2,3] })

          Cheers,
          Tobias

  2. Alex Theedom says:

    A really interesting blog post. Thanks very much. Alex

  3. Rajesh kumar says:

    Hi Hobias Trelle,

    Thanks for the post. I try to create index for nested arrays, but it doesn’t work. Check the below sample code.

    [_id] => MongoId Object (
    [$id] => 53087f483b15eaeb6c3c9869
    )
    [time_from] => 2014-02-22 00:00:00
    [time_to] => 2014-02-22 00:10:00
    [checkin] => Array (
    [0] => Array (
    [time_frame] => 2014-02-22 00:00:56
    [user_id] => 1
    [loc] => Array (
    [type] => Point
    [coordinates] => Array (
    [0] => 73.43
    [1] => 42.22
    )
    )
    )
    [1] => Array (
    [time_frame] => 2014-02-22 00:00:56
    [user_id] => 2
    [loc] => Array (
    [type] => Point
    [coordinates] => Array (
    [0] => 73.10
    [1] => 42.97
    )
    )
    )
    }

    Here i need to create “2dsphere” index for ‘loc’ field.
    I tried like this,

    db..ensureIndex( { “checkin.loc” : “2dsphere”} )

    It would be great if you help me on this.

  4. Dear Rajesh,

    what kind of error is raised?

  5. Rajesh kumar says:

    Dear Tobias Trelle,

    Found the problem, I can now create “2dsphere” index. I need another clarification.

    In single object(document) I am updating user’s checkin(s)[geos] in particular time interval(‘time_from’, ‘time_to’. refer my above example). I need to retrieve users (say user_id:1 )who are all near to the location coordinates in checkin array in same object.

    I tried this

    db.runCommand( { geoNear: , near: {type: “Point”, coordinates: [73.43, 42.22]}, spherical: true, maxDistance: 40000})

    But the result is based on the 1st value of an array ‘checkin’, ignores other values (say for user_id:2)

    Thanks again.

  6. Rodrigo Polo says:

    Using MongoDB version 2.6.0 and mongoose 3.8.8 I realize that “distanceMultiplier” is no longer using radians but mts. so “distanceMultiplier: 1″ will give you meters, assigning “6371” will give you odd results.

  7. alberts says:

    How can i calsulate distance between P1 and P2. And how can i do it with P1 and polygon?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>