mongoid_geo 0.2.6 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.textile CHANGED
@@ -1,10 +1,10 @@
1
1
  h1. Mongoid geo
2
2
 
3
- A Geo extension for Mongoid.
3
+ A Geo extension for Mongoid.
4
4
 
5
5
  "MongoDB Geospatial Indexing":http://www.mongodb.org/display/DOCS/Geospatial+Indexing
6
6
 
7
- * Supports Mongoid 1.7 sphere distance calculations and
7
+ * Supports Mongoid 1.7 sphere distance calculations and
8
8
  * Adds nearSphere inclusion method
9
9
  * Adds a set of geo related inflections
10
10
  * Adds an exta option for defining a "geo" field, to have the generated attr_writer parse and convert strings etc. to float arrays.
@@ -20,7 +20,7 @@ h3. Find addresses near a point
20
20
  h3. Find locations within a circle
21
21
 
22
22
  <pre>
23
- base.where(:location.within => { "$center" => [ [ 50, -40 ], 1 ] })
23
+ base.where(:location.within => { "$center" => [ [ 50, -40 ], 1 ] })
24
24
  </pre>
25
25
 
26
26
  h3. Create geo-spatial index
@@ -28,17 +28,17 @@ h3. Create geo-spatial index
28
28
  <pre>
29
29
  class Person
30
30
  field :location, :type => Array
31
- index [[ :location, Mongo::GEO2D ]], :min => -180, :max => 180
31
+ index [[ :location, Mongo::GEO2D ]], :min => -180, :max => 180
32
32
  end
33
33
 
34
34
  # to ensure indexes are created, either:
35
35
  Mongoid.autocreate_indexes = true
36
36
 
37
37
  # or in the mongoid.yml
38
- autocreate_indexes: true
38
+ autocreate_indexes: true
39
39
  </pre>
40
40
 
41
- These are the only geo features I could find are currently built-in for Mongoid 2.
41
+ These are the only geo features I could find are currently built-in for Mongoid 2.
42
42
  _Mongoid Geo_ implements the following extra features...
43
43
 
44
44
  h1. Mongoid Geo features
@@ -51,9 +51,9 @@ Old/Manual way: @index [[ :location, Mongo::GEO2D ]], :min => -180, :max => 180@
51
51
 
52
52
  Using new _geo_index_ class method : @geo_index :location@
53
53
 
54
- h2. Special geo-array attribute writer
54
+ h2. Special geo-array attribute writer
55
55
 
56
- When setting a geo-location array, the setter should try to convert the value to an array of floats
56
+ When setting a geo-location array, the setter should try to convert the value to an array of floats
57
57
 
58
58
  Old/Manual way:
59
59
 
@@ -64,7 +64,7 @@ Old/Manual way:
64
64
  def locations= args
65
65
  @locations = args.kind_of?(String) ? args.split(",").map(&:to_f) : args
66
66
  end
67
- end
67
+ end
68
68
  </pre>
69
69
 
70
70
  With the new @:geo@ option supplied by _mongoid-geo_ :
@@ -72,29 +72,29 @@ With the new @:geo@ option supplied by _mongoid-geo_ :
72
72
  <pre>
73
73
  class Person
74
74
  field :location, :type => Array, :geo => true
75
-
75
+
76
76
  geo_index :location
77
- end
78
-
77
+ end
78
+
79
79
  p = Person.new
80
80
 
81
81
  # A Geo array can now be set via String or Strings, Hash or Object, here a few examples...
82
82
  # Please see geo_fields_spec.rb for more options!
83
83
 
84
84
  p.location = "45.1, -3.4"
85
- p.location = "45.1", "-3.4"
86
- p.location = {:lat => 45.1, :lng => -3.4}
85
+ p.location = "45.1", "-3.4"
86
+ p.location = {:lat => 45.1, :lng => -3.4}
87
87
  p.location = [{:lat => 45.1, :lng => -3.4}]
88
- p.location = {:latitude => 45.1, :longitude => -3.4}
89
-
88
+ p.location = {:latitude => 45.1, :longitude => -3.4}
89
+
90
90
  my_location = Location.new :latitude => 45.1, :longitude => -3.4
91
91
  p.location = my_location
92
92
 
93
93
  # for each of the above, the following holds
94
- assert([45.1, -3.4], p.location)
94
+ assert([45.1, -3.4], p.location)
95
95
 
96
96
  # also by default adds #lat and #lng convenience methods (thanks to TeuF)
97
-
97
+
98
98
  assert(45.1 , p.lat)
99
99
  assert(-3.4 , p.lng)
100
100
 
@@ -102,12 +102,12 @@ With the new @:geo@ option supplied by _mongoid-geo_ :
102
102
  field :location, :type => Array, :geo => true, :lat => :latitude, :lng => :longitude
103
103
 
104
104
  assert(45.1 , p.latitude)
105
- assert(-3.4 , p.longitude)
106
-
105
+ assert(-3.4 , p.longitude)
106
+
107
107
  # or set the array attributes using symmetric setter convenience methods!
108
108
  p.latitude = 44
109
- assert(44 , p.latitude)
110
-
109
+ assert(44 , p.latitude)
110
+
111
111
  # PURE MAGIC!!!
112
112
  </pre>
113
113
 
@@ -122,9 +122,9 @@ class Address
122
122
 
123
123
  field :location, :type => Array, :geo => true
124
124
  ...
125
- end
125
+ end
126
126
 
127
- # Find all addresses sorted nearest to a specific address loation
127
+ # Find all addresses sorted nearest to a specific address loation
128
128
  nearest_addresses = Address.geoNear(another_address, :location)
129
129
 
130
130
  class Position
@@ -132,16 +132,19 @@ class Position
132
132
 
133
133
  field :pos, :type => Array, :geo => true
134
134
  ...
135
- end
135
+ end
136
136
 
137
- # Find all positions sorted nearest to the address loation
137
+ # Find all positions sorted nearest to the address loation
138
138
  nearest_positions = Position.geoNear(another_address.location, :pos)
139
139
 
140
140
  # perform distance locations in Speherical mode inside Mongo DB (default is :plane)
141
141
  nearest_positions = Position.geoNear(another_address.location, :pos, :mode => :sphere)
142
142
 
143
- # other options supported are: :num, :maxDistance, distanceMultiplier, :query
143
+ # other options supported are: :num, :maxDistance, :distanceMultiplier, :query
144
144
 
145
+ # GeoNear distance returns distance in degrees. Use distanceMultiplier to return in Miles or KM.
146
+ # set distanceMultiplier to 6371 to get distance in KM
147
+ # set distanceMultiplier to 3963.19 to get distance in Miles
145
148
  </pre>
146
149
 
147
150
  If you need to operate on the Mongoid models referenced by the query result, simply call #to_models on it
@@ -153,9 +156,9 @@ You can also use a #to_model method on an individual query result like this:
153
156
 
154
157
  @nearest_city = Position.geoNear(another_address.location, :pos).first.to_model.city@
155
158
 
156
- # the model returned also has a distance accessor populated with the distance calculated by running the geoNear query
159
+ # the model returned also has a distance accessor populated with the distance calculated by running the geoNear query
157
160
 
158
- @nearest_distance = Position.geoNear(another_address.location, :pos).first.to_model.distance@
161
+ @nearest_distance = Position.geoNear(another_address.location, :pos).first.to_model.distance@
159
162
 
160
163
  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 version is set to 1.5. See _geo_near_ specs for more details/info on this.
161
164
 
@@ -196,8 +199,8 @@ Find points near a given point within a maximum distance
196
199
 
197
200
  You can also use a Hash to define the nearMax
198
201
 
199
- <pre>
200
- places.where(:location.nearMax => {:point => [ 72, -44 ], :distance => 5})
202
+ <pre>
203
+ places.where(:location.nearMax => {:point => [ 72, -44 ], :distance => 5})
201
204
  </pre>
202
205
 
203
206
  Or use an Object (which must have the methods @#point@ and @#distance@ that return the point and max distance from that point)
@@ -206,8 +209,8 @@ Or use an Object (which must have the methods @#point@ and @#distance@ that retu
206
209
  near_max_ = (Struct.new :point, :distance).new
207
210
  near_max.point = [50, 40]
208
211
  near_max.distance = [30,55]
209
-
210
- places.where(:location.nearMax => near_max)
212
+
213
+ places.where(:location.nearMax => near_max)
211
214
  </pre>
212
215
 
213
216
  Note: For the points, you can also use a hash or an object with the methods/keys, either @:lat, :lng@ or @:latitude, :longitude@
@@ -217,12 +220,12 @@ Example:
217
220
  <pre>
218
221
  center = (Struct.new :lat, :lng).new
219
222
  center.lat = 72
220
- center.lng = -44
221
- places.where(:location.withinCenter => [center, radius])
222
-
223
+ center.lng = -44
224
+ places.where(:location.withinCenter => [center, radius])
225
+
223
226
  # OR
224
-
225
- places.where(:location.withinCenter => [{:lat => 72, :lng => -44}, radius])
227
+
228
+ places.where(:location.withinCenter => [{:lat => 72, :lng => -44}, radius])
226
229
  </pre>
227
230
 
228
231
  h3. withinBox
@@ -238,12 +241,12 @@ h3. withinBox
238
241
 
239
242
  You can also use a Hash to define the box
240
243
 
241
- <pre>
242
- places.where(:location.withinBox => {:lower_left => [50, 40], :upper_right => [30,55]})
243
-
244
+ <pre>
245
+ places.where(:location.withinBox => {:lower_left => [50, 40], :upper_right => [30,55]})
246
+
244
247
  # or mix and match
245
-
246
- places.where(:location.withinBox => {:lower_left => {:lat => 50, :lng => 40}, :upper_right => [30,55] } )
248
+
249
+ places.where(:location.withinBox => {:lower_left => {:lat => 50, :lng => 40}, :upper_right => [30,55] } )
247
250
  </pre>
248
251
 
249
252
  Or use an object (which must have the methods @#lower_left@ and @#upper_right@ that return the points of the bounding box)
@@ -252,8 +255,8 @@ Or use an object (which must have the methods @#lower_left@ and @#upper_right@ t
252
255
  box = (Struct.new :lower_left, :upper_right).new
253
256
  box.lower_left = [50, 40]
254
257
  box.upper_right = [30, 55]
255
-
256
- places.where(:location.withinBox => box)
258
+
259
+ places.where(:location.withinBox => box)
257
260
  </pre>
258
261
 
259
262
  h3. withinCenter
@@ -262,17 +265,17 @@ h3. withinCenter
262
265
  center = [50, 40]
263
266
  radius = 4
264
267
 
265
- places.where(:location.withinCenter => [center, radius])
266
- # => places: {"$within" : {"$center" : [[50, 40], 4]}
268
+ places.where(:location.withinCenter => [center, radius])
269
+ # => places: {"$within" : {"$center" : [[50, 40], 4]}
267
270
 
268
- places.where(:location.withinCenter(:sphere) => [center, radius])
269
- # => places: {"$within" : {"$centerSphere" : [[50, 40], 4]}
271
+ places.where(:location.withinCenter(:sphere) => [center, radius])
272
+ # => places: {"$within" : {"$centerSphere" : [[50, 40], 4]}
270
273
  </pre>
271
274
 
272
275
  You can also use a hash to define the circle, with @:center@ and @:radius@ keys
273
276
 
274
- <pre>
275
- places.where(:location.withinCenter => {:center => [50, 40], :radius => 4})
277
+ <pre>
278
+ places.where(:location.withinCenter => {:center => [50, 40], :radius => 4})
276
279
  </pre>
277
280
 
278
281
  Or use an object (which must have the methods #lower_left and #upper_right that return the points of the bounding box)
@@ -281,6 +284,6 @@ Or use an object (which must have the methods #lower_left and #upper_right that
281
284
  circle = (Struct.new :center, :radius).new
282
285
  circle.center = [50, 40]
283
286
  circle.radius = 4
284
-
285
- places.where(:location.withinCenter => circle)
287
+
288
+ places.where(:location.withinCenter => circle)
286
289
  </pre>
@@ -1,4 +1,4 @@
1
- require 'net/http'
1
+ #require 'net/http'
2
2
  require 'active_support'
3
3
  require 'hashie'
4
4
  require 'mongoid/geo/haversine'
@@ -8,15 +8,15 @@ module Mongoid
8
8
  class << self
9
9
  attr_accessor :mongo_db_version
10
10
  end
11
-
12
- module Distance
11
+
12
+ module Distance
13
13
  attr_reader :distance
14
-
14
+
15
15
  def set_distance dist
16
16
  @distance = dist
17
17
  end
18
18
  end
19
-
19
+
20
20
  module Model
21
21
  def to_model
22
22
  m = clazz.where(:_id => _id).first.extend(Mongoid::Geo::Distance)
@@ -24,37 +24,38 @@ module Mongoid
24
24
  m
25
25
  end
26
26
  end
27
-
27
+
28
28
  module Models
29
29
  def to_models
30
30
  clazz = first.clazz
31
31
  ids = map(&:_id)
32
- distance_hash = self.inject({}) do |result, item|
32
+ distance_hash = self.inject({}) do |result, item|
33
33
  result[item._id] = item.distance
34
34
  result
35
35
  end
36
- clazz.where(:_id.in => ids).to_a.map do |m|
36
+ ret = clazz.where(:_id.in => ids).to_a.map do |m|
37
37
  m.extend(Mongoid::Geo::Distance)
38
- m.set_distance distance_hash[m._id]
38
+ m.set_distance distance_hash[m._id.to_s]
39
39
  m
40
40
  end
41
+ ret.sort! {|a,b| a.distance <=> b.distance}
41
42
  end
42
43
  end
43
-
44
+
44
45
  module Near
45
46
  def geoNear(center, location_attribute, options = {})
46
47
  center = center.respond_to?(:collection) ? center.send(location_attribute) : center
47
48
  query = create_query(self, center, options)
48
- create_result(query_result(self, query, center, location_attribute)).extend(Mongoid::Geo::Models)
49
+ create_result(query_result(self, query, center, location_attribute, options)).extend(Mongoid::Geo::Models)
49
50
  end
50
51
 
51
52
  protected
52
-
53
+
53
54
  def create_query clazz, center, options = {}
54
55
  num = options[:num]
55
56
  maxDistance = options[:maxDistance]
56
57
  query = options[:query]
57
- distanceMultiplier = options[:distanceMultiplier]
58
+ distanceMultiplier = options[:distanceMultiplier]
58
59
  mode = options[:mode] || :plane
59
60
 
60
61
  nq = BSON::OrderedHash.new.tap do |near_query|
@@ -62,20 +63,25 @@ module Mongoid
62
63
  near_query["near"] = center
63
64
  near_query["num"] = num if num
64
65
  near_query["maxDistance"] = maxDistance if maxDistance
65
- near_query["distanceMultiplier"] = distanceMultiplier if distanceMultiplier
66
+ # mongodb < 1.7 returns degrees but with earth flat. in Mongodb 1.7 you can set sphere and let mongodb calculate the distance in Miles or KM
67
+ # for mongodb < 1.7 we need to run Haversine first before calculating degrees to Km or Miles. See below.
68
+ near_query["distanceMultiplier"] = distanceMultiplier if distanceMultiplier && Mongoid::Geo.mongo_db_version >= 1.7
66
69
  near_query["query"] = query if query
67
70
 
68
71
  # works in mongodb 1.7 but still in beta and not supported by mongodb
69
- near_query["spherical"] = true if mode == :sphere
72
+ near_query["spherical"] = true if mode == :sphere && Mongoid::Geo.mongo_db_version >= 1.7
70
73
  end
71
74
  nq
72
- end
75
+ end
73
76
 
74
- def query_result clazz, query, center, location_attribute
75
- lon,lat = center
77
+ def query_result clazz, query, center, location_attribute, options = {}
78
+ distanceMultiplier = options[:distanceMultiplier]
79
+ lon,lat = center
76
80
  query_result = clazz.collection.db.command(query)['results'].sort_by do |r|
77
81
  loc = r['obj'][location_attribute.to_s]
78
82
  r['distance'] = Mongoid::Geo::Haversine.distance(lat, lon, loc[1], loc[0]) if Mongoid::Geo.mongo_db_version < 1.7
83
+ # Calculate distance in KM or Miles if mongodb < 1.7
84
+ r['distance'] = r['distance'] * distanceMultiplier if distanceMultiplier && Mongoid::Geo.mongo_db_version < 1.7
79
85
  r['clazz'] = clazz
80
86
  end
81
87
  end
@@ -88,7 +94,7 @@ module Mongoid
88
94
  res._id = qr['obj']['_id'].to_s
89
95
  result.push(res)
90
96
  end
91
- end
92
- end
97
+ end
98
+ end
93
99
  end
94
100
  end
@@ -2,7 +2,7 @@ module Mongoid
2
2
  module Geo
3
3
  class Haversine
4
4
  #EARTH_RADIUS = 3963.19 # miles
5
- EARTH_RADIUS = 6371 # kilometers
5
+ # EARTH_RADIUS = 6371 # kilometers
6
6
  RADIAN_PER_DEGREE = Math::PI / 180.0
7
7
 
8
8
  def self.distance(lat1, lng1, lat2, lng2)
@@ -14,7 +14,8 @@ module Mongoid
14
14
 
15
15
  a = Math.sin(distance_lat/2)**2 + Math.cos(lat1_radians) * Math.cos(lat2_radians) * Math.sin(distance_lng/2) ** 2
16
16
  c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a))
17
- EARTH_RADIUS * c
17
+ # EARTH_RADIUS * c #
18
+ c
18
19
  end
19
20
  end
20
21
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongoid_geo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.6
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,12 +9,12 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-03-04 00:00:00.000000000 +01:00
12
+ date: 2011-03-09 00:00:00.000000000 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rspec
17
- requirement: &2156534080 !ruby/object:Gem::Requirement
17
+ requirement: &2154623900 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ! '>='
@@ -22,10 +22,10 @@ dependencies:
22
22
  version: '2.4'
23
23
  type: :development
24
24
  prerelease: false
25
- version_requirements: *2156534080
25
+ version_requirements: *2154623900
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: mongoid
28
- requirement: &2156533620 !ruby/object:Gem::Requirement
28
+ requirement: &2154623440 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
31
  - - ! '>='
@@ -33,10 +33,10 @@ dependencies:
33
33
  version: 2.0.0.rc.6
34
34
  type: :runtime
35
35
  prerelease: false
36
- version_requirements: *2156533620
36
+ version_requirements: *2154623440
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: bson_ext
39
- requirement: &2156533160 !ruby/object:Gem::Requirement
39
+ requirement: &2154622980 !ruby/object:Gem::Requirement
40
40
  none: false
41
41
  requirements:
42
42
  - - ! '>='
@@ -44,10 +44,10 @@ dependencies:
44
44
  version: 1.1.6
45
45
  type: :runtime
46
46
  prerelease: false
47
- version_requirements: *2156533160
47
+ version_requirements: *2154622980
48
48
  - !ruby/object:Gem::Dependency
49
49
  name: activesupport
50
- requirement: &2156532700 !ruby/object:Gem::Requirement
50
+ requirement: &2154622520 !ruby/object:Gem::Requirement
51
51
  none: false
52
52
  requirements:
53
53
  - - ! '>='
@@ -55,10 +55,10 @@ dependencies:
55
55
  version: 3.0.4
56
56
  type: :runtime
57
57
  prerelease: false
58
- version_requirements: *2156532700
58
+ version_requirements: *2154622520
59
59
  - !ruby/object:Gem::Dependency
60
60
  name: hashie
61
- requirement: &2156532240 !ruby/object:Gem::Requirement
61
+ requirement: &2154622060 !ruby/object:Gem::Requirement
62
62
  none: false
63
63
  requirements:
64
64
  - - ! '>='
@@ -66,7 +66,7 @@ dependencies:
66
66
  version: 0.4.0
67
67
  type: :runtime
68
68
  prerelease: false
69
- version_requirements: *2156532240
69
+ version_requirements: *2154622060
70
70
  description: Geo spatial extension on Mongoid 2, to add more geo-spatial capabilities
71
71
  email:
72
72
  - kmandrup@gmail.com