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

11 Comments

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 😉

Author

Tobias Trelle

Tobias Trelle

Senior IT Consultant

Share on FacebookGoogle+Share on LinkedInTweet about this on TwitterShare on RedditDigg thisShare on StumbleUpon

Kommentare

  • March 21, 2013 von Robin Wagner

    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

    • Tobias Trelle

      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

      • March 22, 2013 von Robin Wagner

        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

        • Tobias Trelle

          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

  • A really interesting blog post. Thanks very much. Alex

  • February 24, 2014 von Rajesh kumar

    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.

  • Tobias Trelle

    Dear Rajesh,

    what kind of error is raised?

  • February 27, 2014 von Rajesh kumar

    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.

    • Tobias Trelle

      When you query a document based on array elements, you will always find the document no matter what array element exactly was hit be query.

  • 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.

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

Comment

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