mongoid_geospatial 2.3.0 → 2.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +155 -90
- data/lib/mongoid_geospatial/fields/geometry_field.rb +2 -2
- data/lib/mongoid_geospatial/fields/point.rb +7 -1
- data/lib/mongoid_geospatial/geospatial.rb +10 -1
- data/lib/mongoid_geospatial/version.rb +1 -1
- data/spec/models/bar.rb +2 -0
- data/spec/mongoid_geospatial/field_option_spec.rb +6 -1
- data/spec/mongoid_geospatial/fields/box_spec.rb +11 -0
- data/spec/mongoid_geospatial/fields/point_spec.rb +30 -1
- metadata +4 -2
data/README.md
CHANGED
@@ -3,7 +3,6 @@ Mongoid Geospatial
|
|
3
3
|
|
4
4
|
A Mongoid Extension that simplifies the use of MongoDB spatial features.
|
5
5
|
|
6
|
-
|
7
6
|
** On beta again **
|
8
7
|
|
9
8
|
Removing some trash, improving and adding support for RGeo and GeoRuby.
|
@@ -25,98 +24,26 @@ You can also use an external Geometric/Spatial alongside.
|
|
25
24
|
# Gemfile
|
26
25
|
gem 'mongoid_geospatial'
|
27
26
|
|
28
|
-
|
29
27
|
# A place to illustrate Point, Line and Polygon
|
30
28
|
class Place
|
31
29
|
include Mongoid::Document
|
30
|
+
|
31
|
+
# Include the module
|
32
32
|
include Mongoid::Geospatial
|
33
33
|
|
34
|
+
# Just like mongoid,
|
34
35
|
field :name, type: String
|
36
|
+
# define your field, but choose a geometry type:
|
35
37
|
field :location, type: Point, :spatial => true
|
36
38
|
field :route, type: Linestring
|
37
39
|
field :area, type: Polygon
|
40
|
+
|
41
|
+
# If your are going to query on your points, don't forget to index:
|
42
|
+
spatial_index :location
|
43
|
+
|
38
44
|
end
|
39
45
|
|
40
46
|
|
41
|
-
Geometry Helpers
|
42
|
-
----------------
|
43
|
-
|
44
|
-
We currently support GeoRuby and RGeo.
|
45
|
-
If you require one of those, a #to_geo method will be available to all
|
46
|
-
spatial fields, returning the external library corresponding object.
|
47
|
-
To illustrate:
|
48
|
-
|
49
|
-
class Person
|
50
|
-
include Mongoid::Document
|
51
|
-
include Mongoid::Geospatial
|
52
|
-
|
53
|
-
field :location, type: Point
|
54
|
-
end
|
55
|
-
|
56
|
-
me = Person.new(location: [8, 8])
|
57
|
-
|
58
|
-
# Example with GeoRuby
|
59
|
-
point.class # Mongoid::Geospatial::Point
|
60
|
-
point.to_geo.class # GeoRuby::SimpleFeatures::Point
|
61
|
-
|
62
|
-
# Example with RGeo
|
63
|
-
point.class # Mongoid::Geospatial::Point
|
64
|
-
point.to_geo.class # RGeo::Geographic::SphericalPointImpl
|
65
|
-
|
66
|
-
|
67
|
-
Configure
|
68
|
-
----------------
|
69
|
-
|
70
|
-
Assemble it as you need (use a initializer file):
|
71
|
-
|
72
|
-
With RGeo
|
73
|
-
|
74
|
-
Mongoid::Geospatial.use_rgeo
|
75
|
-
# Optional
|
76
|
-
# Mongoid::Geospatial.factory = RGeo::Geographic.spherical_factory
|
77
|
-
|
78
|
-
With GeoRuby
|
79
|
-
|
80
|
-
Mongoid::Geospatial.use_georuby
|
81
|
-
|
82
|
-
Defaults (change if you know what you're doing)
|
83
|
-
|
84
|
-
Mongoid::Geospatial.lng_symbol = :x
|
85
|
-
Mongoid::Geospatial.lat_symbol = :y
|
86
|
-
Mongoid::Geospatial.earth_radius = EARTH_RADIUS
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
Model Setup
|
91
|
-
-----------
|
92
|
-
|
93
|
-
You can create Point, Line, Circle, Box and Polygon on your models:
|
94
|
-
|
95
|
-
|
96
|
-
class River
|
97
|
-
include Mongoid::Document
|
98
|
-
include Mongoid::Geospatial
|
99
|
-
|
100
|
-
field :name, type: String
|
101
|
-
field :length, type: Integer
|
102
|
-
field :discharge, type: Integer
|
103
|
-
|
104
|
-
field :source, type: Point, spatial: true
|
105
|
-
field :mouth, type: Point, spatial: true
|
106
|
-
field :course, type: Line
|
107
|
-
field :boundings, type: Box
|
108
|
-
|
109
|
-
# spatial indexing
|
110
|
-
spatial_index :mouth
|
111
|
-
|
112
|
-
# default mongodb options
|
113
|
-
spatial_index :mouth, {bit: 24, min: -180, max: 180}
|
114
|
-
end
|
115
|
-
|
116
|
-
|
117
|
-
Use
|
118
|
-
---
|
119
|
-
|
120
47
|
Generate indexes on MongoDB:
|
121
48
|
|
122
49
|
|
@@ -126,6 +53,10 @@ Generate indexes on MongoDB:
|
|
126
53
|
Points
|
127
54
|
------
|
128
55
|
|
56
|
+
Currently, MongoDB supports query operations on 2D points only, so that's
|
57
|
+
what this lib does. All geometries apart from points are just arrays
|
58
|
+
in the database. Here's is how you can input a point as:
|
59
|
+
|
129
60
|
* an unordered hash with the lat long string keys defined when setting the field (only applies for setting the field)
|
130
61
|
* longitude latitude array in that order - [long,lat] ([x, y])
|
131
62
|
* an unordered hash with latitude key(:lat, :latitude) and a longitude key(:lon, :long, :lng, :longitude)
|
@@ -150,10 +81,18 @@ We store data in the DB as a [lng,lat] array then reformat when it is returned t
|
|
150
81
|
Now to access this spatial information we can do this
|
151
82
|
|
152
83
|
hudson.mouth # => [-74.026667, 40.703056]
|
84
|
+
|
85
|
+
If you need a hash
|
86
|
+
|
87
|
+
hudson.mouth.to_hsh # => { x: -74.026667, y: 40.703056 }
|
88
|
+
|
89
|
+
If you are using GeoRuby or RGeo
|
90
|
+
|
91
|
+
hudson.mouth.to_geo # => NiceGeolib::Point
|
153
92
|
|
154
93
|
Distance and other geometrical calculations are delegated to the external
|
155
94
|
library you choosed. More info about using RGeo or GeoRuby below.
|
156
|
-
Some built in helpers:
|
95
|
+
Some built in helpers for mongoid queries:
|
157
96
|
|
158
97
|
# Returns middle point + radius
|
159
98
|
# Useful to search #within_circle
|
@@ -165,6 +104,12 @@ Some built in helpers:
|
|
165
104
|
hudson.mounth.to_hsh(:lon, :lat) # {:lon => -74.., :lat => 40..}
|
166
105
|
|
167
106
|
|
107
|
+
And for polygons and lines:
|
108
|
+
|
109
|
+
house.area.bbox # Returns polygon bounding_box (envelope)
|
110
|
+
house.area.center # Returns calculate middle point
|
111
|
+
|
112
|
+
|
168
113
|
Query
|
169
114
|
--------
|
170
115
|
|
@@ -178,30 +123,150 @@ All MongoDB queries are handled by Mongoid.
|
|
178
123
|
You can use Geometry instance directly on any query:
|
179
124
|
|
180
125
|
* near
|
126
|
+
* Bar.near(location: person.house)
|
181
127
|
* Bar.where(:location.near => person.house)
|
128
|
+
|
182
129
|
|
183
130
|
* near_sphere
|
131
|
+
* Bar.near_sphere(location: person.house)
|
184
132
|
* Bar.where(:location.near_sphere => person.house)
|
185
133
|
|
134
|
+
|
186
135
|
* within_box
|
187
|
-
* Bar.
|
136
|
+
* Bar.within_box(location: hood.area)
|
137
|
+
|
188
138
|
|
189
139
|
* within_circle
|
190
|
-
* Bar.
|
140
|
+
* Bar.within_circle(location: hood.area)
|
141
|
+
|
191
142
|
|
192
143
|
* within_circle_sphere
|
193
|
-
* Bar.
|
144
|
+
* Bar.within_circle_sphere(location: hood.area)
|
145
|
+
|
194
146
|
|
195
147
|
* within_polygon
|
196
|
-
* Bar.
|
148
|
+
* Bar.within_polygon(location: city.area)
|
149
|
+
|
150
|
+
|
151
|
+
External Libraries
|
152
|
+
------------------
|
153
|
+
|
154
|
+
Use RGeo?
|
155
|
+
https://github.com/dazuma/rgeo
|
156
|
+
|
157
|
+
RGeo is a Ruby wrapper for Proj/GEOS.
|
158
|
+
It's perfect when you need to work with complex calculations and projections.
|
159
|
+
It'll require more stuff installed to compile/work.
|
160
|
+
|
161
|
+
|
162
|
+
Use GeoRuby?
|
163
|
+
https://github.com/nofxx/geo_ruby
|
164
|
+
|
165
|
+
GeoRuby is a pure Ruby Geometry Library.
|
166
|
+
It's perfect if you want simple calculations and/or keep your stack in pure ruby.
|
167
|
+
Albeit not full featured in maths it has a handful of methods and good import/export helpers.
|
168
|
+
|
169
|
+
Use Nothing?
|
170
|
+
|
171
|
+
This lib won't stand in your way.
|
172
|
+
Write your own wrapper if you want.
|
173
|
+
|
174
|
+
|
175
|
+
Geometry Helpers
|
176
|
+
----------------
|
177
|
+
|
178
|
+
We currently support GeoRuby and RGeo.
|
179
|
+
If you require one of those, a #to_geo method will be available to all
|
180
|
+
spatial fields, returning the external library corresponding object.
|
181
|
+
To illustrate:
|
182
|
+
|
183
|
+
class Person
|
184
|
+
include Mongoid::Document
|
185
|
+
include Mongoid::Geospatial
|
186
|
+
|
187
|
+
field :location, type: Point
|
188
|
+
end
|
189
|
+
|
190
|
+
me = Person.new(location: [8, 8])
|
191
|
+
|
192
|
+
# Example with GeoRuby
|
193
|
+
point.class # Mongoid::Geospatial::Point
|
194
|
+
point.to_geo.class # GeoRuby::SimpleFeatures::Point
|
195
|
+
|
196
|
+
# Example with RGeo
|
197
|
+
point.class # Mongoid::Geospatial::Point
|
198
|
+
point.to_geo.class # RGeo::Geographic::SphericalPointImpl
|
199
|
+
|
200
|
+
|
201
|
+
Configure
|
202
|
+
----------------
|
203
|
+
|
204
|
+
Assemble it as you need (use a initializer file):
|
205
|
+
|
206
|
+
With RGeo
|
207
|
+
|
208
|
+
Mongoid::Geospatial.use_rgeo
|
209
|
+
# Optional
|
210
|
+
# Mongoid::Geospatial.factory = RGeo::Geographic.spherical_factory
|
211
|
+
|
212
|
+
|
213
|
+
With GeoRuby
|
214
|
+
|
215
|
+
Mongoid::Geospatial.use_georuby
|
216
|
+
|
217
|
+
|
218
|
+
Defaults (change if you know what you're doing)
|
219
|
+
|
220
|
+
Mongoid::Geospatial.lng_symbol = :x
|
221
|
+
Mongoid::Geospatial.lat_symbol = :y
|
222
|
+
Mongoid::Geospatial.earth_radius = EARTH_RADIUS
|
223
|
+
|
224
|
+
|
225
|
+
|
226
|
+
Model Setup
|
227
|
+
-----------
|
228
|
+
|
229
|
+
You can create Point, Line, Circle, Box and Polygon on your models:
|
230
|
+
|
231
|
+
|
232
|
+
class CrazyGeom
|
233
|
+
include Mongoid::Document
|
234
|
+
include Mongoid::Geospatial
|
235
|
+
|
236
|
+
field :location, type: Point
|
237
|
+
|
238
|
+
field :route, type: Line
|
239
|
+
field :area, type: Polygon
|
240
|
+
|
241
|
+
field :square, type: Box
|
242
|
+
field :around, type: Circle
|
243
|
+
|
244
|
+
# spatial indexing
|
245
|
+
spatial_index :mouth
|
246
|
+
|
247
|
+
# default mongodb options
|
248
|
+
spatial_index :mouth, {bit: 24, min: -180, max: 180}
|
249
|
+
|
250
|
+
# query by location
|
251
|
+
spatial_scope :location
|
252
|
+
end
|
253
|
+
|
254
|
+
|
255
|
+
|
256
|
+
Nearby
|
257
|
+
------
|
197
258
|
|
259
|
+
You can add a `spatial_scope` on your models. So you can query:
|
198
260
|
|
199
|
-
|
200
|
-
|
261
|
+
Bar.nearby(my.location)
|
262
|
+
|
263
|
+
instead of
|
201
264
|
|
202
|
-
|
265
|
+
Bar.near(location: my.location)
|
266
|
+
|
267
|
+
Good when you're drunk. Just add to your model:
|
203
268
|
|
204
|
-
field
|
269
|
+
spatial_scope :<field>
|
205
270
|
|
206
271
|
|
207
272
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module Mongoid
|
2
2
|
module Geospatial
|
3
3
|
class Point
|
4
|
+
include Enumerable
|
4
5
|
attr_accessor :x, :y
|
5
6
|
|
6
7
|
def initialize(x=nil, y=nil)
|
@@ -18,6 +19,11 @@ module Mongoid
|
|
18
19
|
mongoize[args]
|
19
20
|
end
|
20
21
|
|
22
|
+
def each
|
23
|
+
yield x
|
24
|
+
yield y
|
25
|
+
end
|
26
|
+
|
21
27
|
def to_hsh xl = :x, yl = :y
|
22
28
|
{xl => x, yl => y}
|
23
29
|
end
|
@@ -58,7 +64,7 @@ module Mongoid
|
|
58
64
|
|
59
65
|
# Database -> Object
|
60
66
|
def demongoize(object)
|
61
|
-
|
67
|
+
return unless object
|
62
68
|
Point.new(*object)
|
63
69
|
end
|
64
70
|
|
@@ -26,7 +26,7 @@ module Mongoid
|
|
26
26
|
@@earth_radius = EARTH_RADIUS.dup
|
27
27
|
|
28
28
|
included do
|
29
|
-
attr_accessor :geo
|
29
|
+
# attr_accessor :geo
|
30
30
|
cattr_accessor :spatial_fields, :spatial_fields_indexed
|
31
31
|
@@spatial_fields = []
|
32
32
|
@@spatial_fields_indexed = []
|
@@ -51,6 +51,15 @@ module Mongoid
|
|
51
51
|
index({name => '2d'}, options)
|
52
52
|
end
|
53
53
|
|
54
|
+
def spatial_scope field, opts = {}
|
55
|
+
self.singleton_class.class_eval do
|
56
|
+
# define_method(:close) do |args|
|
57
|
+
define_method(:nearby) do |args|
|
58
|
+
queryable.where(field.near_sphere => args)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
54
63
|
end
|
55
64
|
|
56
65
|
end
|
data/spec/models/bar.rb
CHANGED
@@ -3,9 +3,14 @@ require "spec_helper"
|
|
3
3
|
describe Mongoid::Fields do
|
4
4
|
|
5
5
|
context "spatial" do
|
6
|
+
before do
|
7
|
+
Bar.create_indexes
|
8
|
+
end
|
6
9
|
|
7
10
|
it "should set some class methods" do
|
8
|
-
Bar.
|
11
|
+
far = Bar.create!(name: "Far", location: [7,7])
|
12
|
+
near = Bar.create!(name: "Near", location: [2,2])
|
13
|
+
Bar.nearby([1,1]).should eq([near, far])
|
9
14
|
end
|
10
15
|
|
11
16
|
end
|
@@ -9,7 +9,14 @@ describe Mongoid::Geospatial::Point do
|
|
9
9
|
|
10
10
|
it "should not fail if point is nil" do
|
11
11
|
bar = Bar.create!(name: "Moe's")
|
12
|
-
bar.location.
|
12
|
+
bar.location.should be_nil
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should set point to nil" do
|
16
|
+
bar = Bar.create!(name: "Moe's", location: [1, 1])
|
17
|
+
bar.location = nil
|
18
|
+
bar.location.should be_nil
|
19
|
+
bar.save.should be_true
|
13
20
|
end
|
14
21
|
|
15
22
|
describe "methods" do
|
@@ -36,6 +43,14 @@ describe Mongoid::Geospatial::Point do
|
|
36
43
|
bar.location.radius_sphere[1].should be_within(0.0001).of(0.00015)
|
37
44
|
end
|
38
45
|
|
46
|
+
it "should have a radius sphere helper in meters" do
|
47
|
+
bar.location.radius_sphere(1000, :m)[1].should be_within(0.0001).of(0.00015)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should have a radius sphere helper in miles" do
|
51
|
+
bar.location.radius_sphere(1, :mi)[1].should be_within(0.0001).of(0.00025)
|
52
|
+
end
|
53
|
+
|
39
54
|
end
|
40
55
|
|
41
56
|
describe "queryable" do
|
@@ -66,6 +81,15 @@ describe Mongoid::Geospatial::Point do
|
|
66
81
|
Bar.where(:location.near => jim.location).should == [ paris, prague, berlin ]
|
67
82
|
end
|
68
83
|
|
84
|
+
it "returns the documents sorted closest to furthest" do
|
85
|
+
Bar.near(location: jim.location).should == [ paris, prague, berlin ]
|
86
|
+
end
|
87
|
+
|
88
|
+
it "returns the documents sorted closest to furthest sphere" do
|
89
|
+
person = Person.new(:location => [ 41.23, 2.9 ])
|
90
|
+
Bar.near_sphere(location: jim.location).should == [ paris, prague, berlin ]
|
91
|
+
end
|
92
|
+
|
69
93
|
it "returns the documents sorted closest to furthest sphere" do
|
70
94
|
person = Person.new(:location => [ 41.23, 2.9 ])
|
71
95
|
Bar.where(:location.near_sphere => jim.location).should == [ paris, prague, berlin ]
|
@@ -113,6 +137,11 @@ describe Mongoid::Geospatial::Point do
|
|
113
137
|
it "returns the documents within a center_sphere" do
|
114
138
|
Bar.where(:location.within_spherical_circle => [elvis.location, 0.5]).to_a.should include(mile9)
|
115
139
|
end
|
140
|
+
|
141
|
+
it "returns the documents within a box" do
|
142
|
+
Bar.within_box(location: [elvis.location, elvis.location.map(&:ceil)]).to_a.should == [ mile3 ]
|
143
|
+
end
|
144
|
+
|
116
145
|
end
|
117
146
|
|
118
147
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongoid_geospatial
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.5.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-09-
|
13
|
+
date: 2012-09-14 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rgeo
|
@@ -181,6 +181,7 @@ files:
|
|
181
181
|
- spec/models/river.rb
|
182
182
|
- spec/mongoid_geospatial/extensions/core_ext_spec.rb
|
183
183
|
- spec/mongoid_geospatial/field_option_spec.rb
|
184
|
+
- spec/mongoid_geospatial/fields/box_spec.rb
|
184
185
|
- spec/mongoid_geospatial/fields/circle_spec.rb
|
185
186
|
- spec/mongoid_geospatial/fields/line_spec.rb
|
186
187
|
- spec/mongoid_geospatial/fields/point_spec.rb
|
@@ -227,6 +228,7 @@ test_files:
|
|
227
228
|
- spec/models/river.rb
|
228
229
|
- spec/mongoid_geospatial/extensions/core_ext_spec.rb
|
229
230
|
- spec/mongoid_geospatial/field_option_spec.rb
|
231
|
+
- spec/mongoid_geospatial/fields/box_spec.rb
|
230
232
|
- spec/mongoid_geospatial/fields/circle_spec.rb
|
231
233
|
- spec/mongoid_geospatial/fields/line_spec.rb
|
232
234
|
- spec/mongoid_geospatial/fields/point_spec.rb
|