mongoid_geo 0.2.6 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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