Geo-spatial extension for Mongoid 2
Ruby JavaScript
Switch branches/tags
Nothing to show
Pull request Compare This branch is 3 commits ahead, 42 commits behind kristianmandrup:master.
Failed to load latest commit information.
sandbox added #create_index methods May 21, 2011
spec proposed changes not nearly finished Jun 7, 2011
.document added document file May 22, 2011
.gitignore moved files around Jun 7, 2011
.rvmrc proposed changes not nearly finished Jun 7, 2011
Gemfile updates gemspec to validity Jul 11, 2011
MIT-LICENSE initial commit Feb 4, 2011
README.textile status updates to README and Changelog May 31, 2011
VERSION works with geo_calc and geo_vectors May 31, 2011
mongoid_geo-0.6.0.gem fetches and merges updates from forked original Jul 11, 2011
mongoid_geo.gemspec updates gemspec to validity Jul 11, 2011


Mongoid geo

A Geo extension for Mongoid.

MongoDB Geospatial Indexing

  • Supports Mongo DB 1.7+ sphere distance calculations
  • Extra geo calculation methods such as #near_sphere etc.
  • Add concept of a special “geo” field (for setting the location point)
  • Configure and create geo indexes
  • Calculate locations near a given point and return as criteria, sorted by distance

Status Update (May 31, 2011)

Added support for using GeoPoint from geo_calc to pass around location points. Also makes it easy to use GeoVectors from geo_vectors

Please see new specs use_geo_calc and use_geo_vectors in the /spec folder to see how this is used. Please help make sure the functionality works in the right way!
Thanks :)

Status Update (May 25, 2011)

  • Fixed major bug which stored coordinate in [:lat, :lng]. This is incorrect. Coordinates must be stored with :lng as the first value in order for geospatial queries to work properly.
    - Excerpt from MongoDB GeoSpatial Indexing
    ‘The code assumes that you are using decimal degrees in (longitude, latitude) order. This is the same order used for the GeoJSON spec.
    Using (latitude, longitude) will result in very incorrect results, but is often the ordering used elsewhere, so it is good to double-check.
    The names you assign to a location object (if using an object and not an array) are completely ignored, only the ordering is detected.’

Status update (May 21, 2011)

  • Created alias methods such as #near_sphere for #nearSphere etc. to make the DSL much more ‘rubyish’!
  • Added #create_index! method (see ‘Geo index’ section below).

You need help?

Post questions in the mongoid-geo group. Here I (and other uses of mongoid-geo) will
try to help out and answer any questions.

Suggestions, improvements and bugs?

Please raise issues or suggestions for improvements in the “Issues” section on github.
I would recommend that you try to branch the project, try to fix it yourself and make a pull request.

Current integrations

Mongoid geo has now been integrated with Google-Maps-for-Rails, thanks to oli-g, see commit

Mongoid 2 geo features

The following summarized what geo functionality is already provided by Mongoid 2.0 (as far as I could tell, May 9th, 2011)

Find addresses near a point

  Address.near(:latlng => [37.761523, -122.423575, 1])

Find locations within a circle

  base.where(:location.within => { "$center" => [ [ 50, -40 ], 1 ] })

Create geo-spatial index

  class Person
    field :location, :type => Array
    index [[ :location, Mongo::GEO2D ]], :min => -180, :max => 180

  # to ensure indexes are created, either:
  Mongoid.autocreate_indexes = true

  # or in the mongoid.yml
  autocreate_indexes: true

These are the only geo features I could find that are currently built-in for Mongoid 2.

Mongoid Geo implements some nice extra geo features:

Mongoid Geo features

The following briefly demonstrates all the features that mongoid-geo provides:

Geo index

A new geo_index class method

Usage example:

geo_index :location

Note: For embedded documents, you must define the index in the root collection class. (davemitchell)

Calling geo_index also adds a #create_index! method to the class to enable construction of the index (on the instances/data in the DB).

  class Address
    geo_index :location

I also added a nice little Array macro so you can do this:

  [Address, Location].create_geo_indexes!

Geo option for Array GPS location field

Objective: When setting a geo GPS location array, the setter should try to convert the value to an array of floats

The “old” manual way:

  class Person
    field :locations, :type => Array

    def locations= args
      @locations = args.kind_of?(String) ? args.split(",").map(&:to_f) : args

mongoid-geo provides a new :geo option that can be used with any Array field:

Usage example:

  class Person
    field :location, :type => Array, :geo => true

    geo_index :location

   p =

   # A Geo array can now be set via String or Strings, Hash or Object, here a few examples...
   # Please see geo_fields_spec.rb for more options!

   p.location = "45.1, -3.4"
   p.location = "45.1", "-3.4"
   p.location = {:lat => 45.1, :lng => -3.4}
   p.location = [{:lat => 45.1, :lng => -3.4}]
   p.location = {:latitude => 45.1, :longitude => -3.4}  

   my_location  = :latitude => 45.1, :longitude => -3.4
   p.location   = my_location

   # for each of the above, the following holds
   assert([45.1, -3.4], p.location)

   # also by default adds #lat and #lng convenience methods (thanks to TeuF)
   assert(45.1 ,
   assert(-3.4 , p.lng)

Customizing lat/lng attribute names:

    # the #lat and #lng convenience methods can also be customized with the :lat and :lng options
   field :location, :type => Array, :geo => true, :lat => :latitude, :lng => :longitude

   assert(45.1 , p.latitude)
   assert(-3.4 , p.longitude)

   # or set the array attributes using symmetric setter convenience methods!
   p.latitude   = 44
   assert(44 , p.latitude)

Reversing lat/lng for spherical calculations

  # You can also reverse the lat/lng positioning of the array storage - this is fx useful for spherical calculations
  Mongoid::Geo.spherical_mode do
    # Mongoid::Geo.lat_index.should == 1
    # Mongoid::Geo.lng_index.should == 0
    address.location = "23.5, -47"
    address.location.should == [23.5, -47].reverse

  # or alternatively 
  Mongoid::Geo.spherical = true
  address.location = "23.5, -47"
  address.location.should == [23.5, -47].reverse  


class Address
  include Mongoid::Document
  extend Mongoid::Geo::Near

  field :location, :type => Array, :geo => true

# Find all addresses sorted nearest to a specific address loation
nearest_addresses = Address.geo_near(another_address, :location)

class Position
  include Mongoid::Document

  field :pos, :type => Array, :geo => true

Find all positions sorted nearest to the address loation

nearest_positions = Position.geo_near(another_address.location, :pos)

Perform distance locations in Speherical mode inside Mongo DB (default is :plane)

nearest_positions = Position.geo_near(another_address.location, :pos, :mode => :sphere)

Other options supported are: :num, :maxDistance, :distanceMultiplier, :query

GeoNear returns each distance calculated in degrees. Use the :distanceMultiplier or :unit option to return in the unit of your choice (see unit.rb).

Set :distanceMultiplier = 6371 to get distance in KMs
Set @:distanceMultiplier = @3963.19 to get distance in Miles

You can also use the :unit option instead like this (supports :feet, :meters, :kms, :miles):

results = Address.geo_near @center.location, :location, :unit => :feet, :dist_order => :desc

The geo_near query result is returned as a Mongoid::Criteria


Note that the :fromLocation field, stores the location the distance was last calculated as a Hash of the GPS location point it was calculated from:

[23.5, -47].hash

This hash can be retrieved (and used for comparison?) using the fromHash field

from = results.first.fromHash

You can also at any time get the GPS location point which the distance of any result instance was calculated from, using the @fromPoint field

from = results.first.fromPoint

You can now explicitly set/configure the Mongo DB version used. This will affect whether built-in Mongo DB distance calculation will be used or using standalone Ruby Haversine algorithm. By default the Mongo DB version is set to 1.8 (as of May 9, 2011) . See geo_near specs for more details/info on this.

Mongoid::Geo.mongo_db_version = 1.7

Mongoid Geo extra inclusions

Find addresses near a point using spherical distance calculation

  Address.near_sphere(:location => [ 72, -44 ])

Mongoid Geo extra inflections


  base.where(:location.near_sphere => [ 72, -44 ])
  # => :location => { "$nearSphere" : [ 72, -44 ] }


Find points near a given point within a maximum distance

  base.where(:location.near_max => [[ 72, -44 ], 5])
  # => { $near: [50, 40] , $maxDistance: 3 }

  base.where(:location.near_max(:sphere) => [[ 72, -44 ], 5])
  # => { $nearSphere: [50, 40] , $maxDistanceSphere: 3 }

  base.where(:location.near_max(:sphere, :flat) => [[ 72, -44 ], 5])
  # => { $nearSphere: [50, 40] , $maxDistance: 3 }

You can also use a Hash to define the near_max

  places.where(:location.near_max => {:point => [ 72, -44 ], :distance => 5})

Or use an Object (which must have the methods #point and #distance that return the point and max distance from that point)

  near_max_ = ( :point, :distance).new
  near_max.point = [50, 40]
  near_max.distance = [30,55]

  places.where(:location.near_max => near_max)

Note: For the points, you can also use a hash or an object with the methods/keys, either :lat, :lng or :latitude, :longitude


  center = ( :lat, :lng).new = 72
  center.lng = -44
  places.where(:location.within_center => [center, radius])

  # OR

  places.where(:location.within_center => [{:lat => 72, :lng => -44}, radius])


  box = [[50, 40], [30,55]]
  base.where(:location.within_box => box)
  # => locations: {"$within" : {"$box" : [[50, 40], [30,55]]}

  base.where(:location.within_box(:sphere) => box)
  # => locations: {"$within" : {"$boxSphere" : [[50, 40], [30,55]]}

You can also use a Hash to define the box

  places.where(:location.within_box => {:lower_left => [50, 40], :upper_right => [30,55]})

  # or mix and match

  places.where(:location.within_box => {:lower_left => {:lat => 50, :lng => 40}, :upper_right => [30,55] } )

Or use an object (which must have the methods #lower_left and #upper_right that return the points of the bounding box)

  box = ( :lower_left, :upper_right).new
  box.lower_left =  [50, 40]
  box.upper_right = [30, 55]

  places.where(:location.within_box => box)


  center = [50, 40]
  radius = 4

  places.where(:location.within_center => [center, radius])
  # => places: {"$within" : {"$center" : [[50, 40], 4]}

  places.where(:location.within_center(:sphere) => [center, radius])
  # => places: {"$within" : {"$centerSphere" : [[50, 40], 4]}

You can also use a hash to define the circle, with :center and :radius keys

  places.where(:location.within_center => {:center => [50, 40], :radius => 4})

Or use an object (which must have the methods #center and #radius that return the center and radius of the circle))

  circle = ( :center, :radius).new = [50, 40]
  circle.radius = 4

  places.where(:location.within_center => circle)

Note on the specs

The specs still use the old “Javascript” like method convention, such as #nearSphere
Don’t let that fool you ;)


Please feel free to contribute to the project!

I aim to deliver a complete geo package for use with Mongoid_. This gem should work nicely with geo_calc and geo_vectorsvectors that I’m also working on.

Your assistance on any of these projects will be greatly appreciated :)