mongoid_geospatial 2.0.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +2 -1
- data/README.md +84 -60
- data/lib/mongoid_geospatial.rb +3 -7
- data/lib/mongoid_geospatial/{geospatial → extensions}/core_ext.rb +0 -0
- data/lib/mongoid_geospatial/field_option.rb +3 -4
- data/lib/mongoid_geospatial/fields/geometry_field.rb +34 -0
- data/lib/mongoid_geospatial/fields/line_string.rb +5 -7
- data/lib/mongoid_geospatial/fields/point.rb +18 -33
- data/lib/mongoid_geospatial/fields/polygon.rb +6 -14
- data/lib/mongoid_geospatial/geospatial.rb +20 -25
- data/lib/mongoid_geospatial/version.rb +1 -1
- data/lib/mongoid_geospatial/wrappers/georuby.rb +28 -0
- data/lib/mongoid_geospatial/wrappers/rgeo.rb +32 -0
- data/spec/models/farm.rb +5 -2
- data/spec/models/person.rb +4 -14
- data/spec/models/phone.rb +1 -3
- data/spec/mongoid_geospatial/extensions/core_ext_spec.rb +20 -0
- data/spec/mongoid_geospatial/field_option_spec.rb +11 -0
- data/spec/mongoid_geospatial/fields/line_string_spec.rb +40 -0
- data/spec/mongoid_geospatial/fields/point_spec.rb +136 -10
- data/spec/mongoid_geospatial/fields/polygon_spec.rb +75 -0
- data/spec/mongoid_geospatial/geospatial_spec.rb +88 -3
- data/spec/mongoid_geospatial/wrappers/rgeo_spec.rb +43 -0
- data/spec/spec_helper.rb +5 -21
- metadata +14 -26
- data/lib/mongoid_geospatial/contextual/mongo.rb +0 -118
- data/lib/mongoid_geospatial/criteria.rb +0 -10
- data/lib/mongoid_geospatial/criterion/complex.rb +0 -26
- data/lib/mongoid_geospatial/criterion/inclusion.rb +0 -14
- data/lib/mongoid_geospatial/criterion/near_spatial.rb +0 -52
- data/lib/mongoid_geospatial/criterion/within_spatial.rb +0 -62
- data/lib/mongoid_geospatial/extensions/symbol.rb +0 -46
- data/lib/mongoid_geospatial/finders.rb +0 -5
- data/lib/mongoid_geospatial/geospatial/geo_near_results.rb +0 -140
- data/spec/mongoid_geospatial/contextual/mongo_spec.rb +0 -135
- data/spec/mongoid_geospatial/criterion/complex_spec.rb +0 -15
- data/spec/mongoid_geospatial/criterion/inclusion_spec.rb +0 -375
- data/spec/mongoid_geospatial/criterion/near_spatial_spec.rb +0 -39
- data/spec/mongoid_geospatial/criterion/within_spatial_spec.rb +0 -54
- data/spec/mongoid_geospatial/geospatial/geo_near_results_spec.rb +0 -78
- data/spec/mongoid_geospatial/mongoid_geospatial_spec.rb +0 -83
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
Mongoid Geospatial
|
2
2
|
==================
|
3
3
|
|
4
|
-
A Mongoid Extension that simplifies
|
5
|
-
|
4
|
+
A Mongoid Extension that simplifies the use of MongoDB spatial features.
|
5
|
+
|
6
6
|
|
7
7
|
** On beta again **
|
8
8
|
|
@@ -18,13 +18,80 @@ There are no plans to support Mongoid <= 2.0
|
|
18
18
|
|
19
19
|
Quick Start
|
20
20
|
-----------
|
21
|
-
Add mongoid_geospatial to your Gemfile:
|
22
21
|
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
This gem focus on (making helpers for) spatial features MongoDB has.
|
23
|
+
You can also use an external Geometric/Spatial alongside.
|
24
|
+
|
25
|
+
# Gemfile
|
26
|
+
gem 'mongoid_geospatial'
|
27
|
+
|
28
|
+
|
29
|
+
# A place to illustrate Point, LineString and Polygon
|
30
|
+
class Place
|
31
|
+
include Mongoid::Document
|
32
|
+
include Mongoid::Geospatial
|
33
|
+
|
34
|
+
field :name, type: String
|
35
|
+
field :location, type: Point, :spatial => true
|
36
|
+
field :route, type: Linestring
|
37
|
+
field :area, type: Polygon
|
38
|
+
end
|
39
|
+
|
40
|
+
|
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:
|
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, LineString and Polygon on your models:
|
26
94
|
|
27
|
-
Set up some slugs:
|
28
95
|
|
29
96
|
```ruby
|
30
97
|
class River
|
@@ -48,12 +115,6 @@ class River
|
|
48
115
|
end
|
49
116
|
```
|
50
117
|
|
51
|
-
Avaiable data types:
|
52
|
-
|
53
|
-
* Point
|
54
|
-
* LineString
|
55
|
-
* Polygon
|
56
|
-
|
57
118
|
|
58
119
|
Generate indexes on MongoDB:
|
59
120
|
|
@@ -129,47 +190,6 @@ River.where(:name=>'hudson').geo_near({:lat => 40.73083, :lng => -73.99756})
|
|
129
190
|
River.geo_near([-73.99756,40.73083], :max_distance => 4, :unit => :mi, :spherical => true)
|
130
191
|
```
|
131
192
|
|
132
|
-
There are two types of pagination!
|
133
|
-
|
134
|
-
* MongoDB Pagination - Stores start of rows to page limit in memory
|
135
|
-
* Post Query Pagination - Stores all rows in memory
|
136
|
-
|
137
|
-
Post-Result is only minutely slower than MongoDB because MongoDB has to calculate distance for all of the rows anyway. The slow up is in the transfer of data from the database to ruby.
|
138
|
-
|
139
|
-
Post-Result has some advantages that are listed below.
|
140
|
-
|
141
|
-
```ruby
|
142
|
-
# MongoDB pagination
|
143
|
-
# overwrites #skip chain method
|
144
|
-
# :page - pagination will be enabled if set to any variable including nil, pagination will not be enabled if either :per\_page or :paginator is set
|
145
|
-
# :per\_page
|
146
|
-
# :paginator - Choose which paginator to use. [default :arrary]
|
147
|
-
# Prefered method to set is Mongoid::Geospatial.paginator=:array
|
148
|
-
# Available Paginators [:kaminari, :will\_paginate, :array]
|
149
|
-
# The only thing this does really is configure default per\_page so it is only kind of useful
|
150
|
-
River.geo_near([-73.99756,40.73083], :page => 1)
|
151
|
-
```
|
152
|
-
|
153
|
-
```ruby
|
154
|
-
# Post Query Pagination
|
155
|
-
# At carzen we use Post Query Pagination because we need to re-sort our rows after fetching. Pagination is not friendly with re-sorting.
|
156
|
-
# You can jump pages continously without querying the database again.
|
157
|
-
# listens to #limit/:num & #skip before geo\_near
|
158
|
-
# #page(page\_number, opts = {})
|
159
|
-
# opts:
|
160
|
-
# :per\_page
|
161
|
-
# :paginator
|
162
|
-
# #per(per\_page\_number, opts = {})
|
163
|
-
# opts:
|
164
|
-
# :page
|
165
|
-
# :paginator
|
166
|
-
#
|
167
|
-
# both return a GeoNearResults, which is really just a modified Array
|
168
|
-
# #per really just #page but just moves the options around
|
169
|
-
rivers = River.geo_near([-73.99756,40.73083]).sort_by!{|r| r.geo[:distance] * r.multiplier }
|
170
|
-
rivers = rivers.per(25).page(1)
|
171
|
-
rivers.reset! # resets the object to it is original state right after query.
|
172
|
-
```
|
173
193
|
|
174
194
|
Mongo DB 1.9+ New Geo features
|
175
195
|
---------
|
@@ -289,8 +309,7 @@ This Fork
|
|
289
309
|
---------
|
290
310
|
|
291
311
|
This fork is not backwards compatible with 'mongoid_spatial'.
|
292
|
-
This fork delegates
|
293
|
-
As a result, all the GEOS/Proj features are available in Ruby/Mongoid.
|
312
|
+
This fork delegates calculations to the external libs and use Moped.
|
294
313
|
|
295
314
|
Change in your models:
|
296
315
|
|
@@ -320,16 +339,20 @@ Troubleshooting
|
|
320
339
|
|
321
340
|
**Mongo::OperationFailure: can't find special index: 2d**
|
322
341
|
|
323
|
-
Indexes need to be created. Execute command:
|
342
|
+
Indexes need to be created. Execute command:
|
343
|
+
|
344
|
+
rake db:mongoid:create_indexes
|
324
345
|
|
325
346
|
|
326
347
|
Thanks
|
327
|
-
|
348
|
+
------
|
349
|
+
|
328
350
|
* Thanks to Kristian Mandrup for creating the base of the gem and a few of the tests
|
329
351
|
* Thanks to CarZen LLC. for letting me release the code we are using
|
330
352
|
|
331
|
-
Contributing
|
332
|
-
|
353
|
+
Contributing
|
354
|
+
------------
|
355
|
+
|
333
356
|
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
334
357
|
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
335
358
|
* Fork the project
|
@@ -340,5 +363,6 @@ Contributing to mongoid_spatial
|
|
340
363
|
|
341
364
|
Copyright
|
342
365
|
-----------
|
366
|
+
|
343
367
|
Copyright (c) 2011 Ryan Ong. See LICENSE.txt for
|
344
368
|
further details.
|
data/lib/mongoid_geospatial.rb
CHANGED
@@ -1,18 +1,14 @@
|
|
1
|
-
require 'rgeo'
|
2
1
|
require 'mongoid'
|
3
2
|
require 'active_support/core_ext/string/inflections'
|
4
3
|
require 'active_support/concern'
|
5
|
-
require 'mongoid_geospatial/
|
6
|
-
require 'mongoid_geospatial/criteria'
|
7
|
-
require 'mongoid_geospatial/extensions/symbol'
|
4
|
+
require 'mongoid_geospatial/extensions/core_ext'
|
8
5
|
require 'mongoid_geospatial/extensions/rgeo_spherical_point_impl'
|
9
6
|
require 'mongoid_geospatial/field_option'
|
10
7
|
|
11
|
-
|
8
|
+
require 'mongoid_geospatial/fields/geometry_field'
|
12
9
|
|
13
10
|
%w{point polygon line_string}.each do |type|
|
14
|
-
require "
|
11
|
+
require "mongoid_geospatial/fields/#{type}"
|
15
12
|
end
|
16
13
|
|
17
|
-
require 'mongoid_geospatial/finders'
|
18
14
|
require 'mongoid_geospatial/geospatial'
|
File without changes
|
@@ -2,11 +2,10 @@
|
|
2
2
|
|
3
3
|
Mongoid::Fields.option :spatial do |model,field,options|
|
4
4
|
options = {} unless options.kind_of?(Hash)
|
5
|
-
|
6
|
-
|
5
|
+
# x_meth = options[:x] || :x
|
6
|
+
# y_meth = options[:y] || :y
|
7
7
|
model.class_eval do
|
8
|
-
self.spatial_fields ||= []
|
9
|
-
self.spatial_fields << field.name.to_sym if self.spatial_fields.kind_of? Array
|
8
|
+
(self.spatial_fields ||= []) << field.name.to_sym
|
10
9
|
|
11
10
|
define_method "distance_from_#{field.name}" do |*args|
|
12
11
|
self.distance_from(field.name, *args)
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module Geospatial
|
3
|
+
class GeometryField < Array
|
4
|
+
|
5
|
+
def bounding_box
|
6
|
+
max_x, min_x = -Float::MAX, Float::MAX
|
7
|
+
max_y, min_y = -Float::MAX, Float::MAX
|
8
|
+
each do |point|
|
9
|
+
max_y = point[1] if point[1] > max_y
|
10
|
+
min_y = point[1] if point[1] < min_y
|
11
|
+
max_x = point[0] if point[0] > max_x
|
12
|
+
min_x = point[0] if point[0] < min_x
|
13
|
+
end
|
14
|
+
[[min_x, min_y], [max_x, max_y]]
|
15
|
+
end
|
16
|
+
alias :bbox :bounding_box
|
17
|
+
|
18
|
+
def center_point
|
19
|
+
min, max = *bbox
|
20
|
+
[min[0] + max[0] / 2.0, min[1] + max[1] / 2.0]
|
21
|
+
end
|
22
|
+
alias :center :center_point
|
23
|
+
|
24
|
+
def radius r = 1
|
25
|
+
[center, r]
|
26
|
+
end
|
27
|
+
|
28
|
+
def radius_sphere r = 1
|
29
|
+
[center, r.to_f/Mongoid::Geospatial.earth_radius[:km]]
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -1,14 +1,12 @@
|
|
1
1
|
module Mongoid
|
2
2
|
module Geospatial
|
3
|
-
class LineString
|
4
|
-
|
5
|
-
def mongoize
|
6
|
-
to_a
|
7
|
-
end
|
3
|
+
class LineString < GeometryField
|
8
4
|
|
9
5
|
class << self
|
10
|
-
|
11
|
-
|
6
|
+
|
7
|
+
# Database -> Object
|
8
|
+
def demongoize(o)
|
9
|
+
LineString.new(o)
|
12
10
|
end
|
13
11
|
|
14
12
|
end
|
@@ -1,62 +1,47 @@
|
|
1
1
|
module Mongoid
|
2
2
|
module Geospatial
|
3
3
|
class Point
|
4
|
+
attr_accessor :x, :y
|
4
5
|
|
6
|
+
def initialize(x, y)
|
7
|
+
@x, @y = x, y
|
8
|
+
end
|
9
|
+
|
10
|
+
# Object -> Database
|
5
11
|
def mongoize
|
6
12
|
[x, y]
|
7
13
|
end
|
14
|
+
alias :to_a :mongoize
|
15
|
+
alias :to_xy :mongoize
|
16
|
+
|
17
|
+
def [](args)
|
18
|
+
mongoize[args]
|
19
|
+
end
|
8
20
|
|
9
21
|
class << self
|
10
22
|
|
23
|
+
# Database -> Object
|
11
24
|
def demongoize(object)
|
12
|
-
return unless object && !object.empty?
|
13
|
-
|
14
|
-
#["x"], object["y"]
|
25
|
+
# return unless object && !object.empty?
|
26
|
+
Point.new(object[0], object[1])
|
15
27
|
end
|
16
28
|
|
17
29
|
def mongoize(object)
|
18
|
-
#return new.mongoize if object.respond_to?(:x)
|
19
30
|
case object
|
20
31
|
when Point then object.mongoize
|
21
|
-
when
|
32
|
+
when Array then object.to_xy
|
33
|
+
when Hash then object.to_xy
|
22
34
|
else object
|
23
35
|
end
|
24
36
|
end
|
25
37
|
|
26
|
-
# Converts the object that was supplied to a criteria
|
38
|
+
# Converts the object that was supplied to a criteria
|
27
39
|
# into a database friendly form.
|
28
40
|
def evolve(object)
|
29
41
|
object.respond_to?(:x) ? object.mongoize : object
|
30
42
|
end
|
31
43
|
end
|
32
44
|
|
33
|
-
# - self.spacial_fields ||= []
|
34
|
-
# - self.spacial_fields << field.name.to_sym if self.spacial_fields.kind_of? Array
|
35
|
-
# -
|
36
|
-
# - define_method "distance_from_#{field.name}" do |*args|
|
37
|
-
# - self.distance_from(field.name, *args)
|
38
|
-
# - end
|
39
|
-
# -
|
40
|
-
# - define_method field.name do
|
41
|
-
# - output = self[field.name] || [nil,nil]
|
42
|
-
# - output = {lng_meth => output[0], lat_meth => output[1]} unless options[:return_array]
|
43
|
-
# - return options[:class].new(output) if options[:class]
|
44
|
-
# - output
|
45
|
-
# - end
|
46
|
-
# -
|
47
|
-
# - define_method "#{field.name}=" do |arg|
|
48
|
-
# - if arg.kind_of?(Hash) && arg[lng_meth] && arg[lat_meth]
|
49
|
-
# - arg = [arg[lng_meth].to_f, arg[lat_meth].to_f]
|
50
|
-
# - elsif arg.respond_to?(:to_xy)
|
51
|
-
# - arg = arg.to_xy
|
52
|
-
# - end
|
53
|
-
# - self[field.name]=arg
|
54
|
-
# - arg = [nil,nil] if arg.nil?
|
55
|
-
# - return arg[0..1] if options[:return_array]
|
56
|
-
# - h = {lng_meth => arg[0], lat_meth => arg[1]}
|
57
|
-
# - return h if options[:class].blank?
|
58
|
-
# - options[:class].new(h)
|
59
|
-
|
60
45
|
end
|
61
46
|
end
|
62
47
|
end
|
@@ -1,24 +1,16 @@
|
|
1
1
|
module Mongoid
|
2
2
|
module Geospatial
|
3
|
-
class Polygon
|
4
|
-
|
5
|
-
def mongoize
|
6
|
-
self #.flatten
|
7
|
-
end
|
3
|
+
class Polygon < GeometryField
|
8
4
|
|
9
5
|
class << self
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
ring = RGeo::Geographic.spherical_factory.linear_ring points
|
15
|
-
RGeo::Geographic.spherical_factory.polygon ring
|
6
|
+
|
7
|
+
# Database -> Object
|
8
|
+
def demongoize(o)
|
9
|
+
Polygon.new(o)
|
16
10
|
end
|
17
11
|
|
18
|
-
# def evolve(object)
|
19
|
-
# { "$gte" => object.first, "$lte" => object.last }
|
20
|
-
# end
|
21
12
|
end
|
13
|
+
|
22
14
|
end
|
23
15
|
end
|
24
16
|
end
|
@@ -1,6 +1,3 @@
|
|
1
|
-
require 'mongoid_geospatial/geospatial/core_ext'
|
2
|
-
require 'mongoid_geospatial/geospatial/geo_near_results'
|
3
|
-
|
4
1
|
module Mongoid
|
5
2
|
module Geospatial
|
6
3
|
extend ActiveSupport::Concern
|
@@ -9,36 +6,24 @@ module Mongoid
|
|
9
6
|
LAT_SYMBOLS = [:y, :lat, :latitude]
|
10
7
|
|
11
8
|
EARTH_RADIUS_KM = 6371 # taken directly from mongodb
|
9
|
+
RAD_PER_DEG = Math::PI / 180
|
12
10
|
|
13
11
|
EARTH_RADIUS = {
|
14
12
|
:km => EARTH_RADIUS_KM,
|
15
|
-
:m => EARTH_RADIUS_KM*1000,
|
16
|
-
:mi => EARTH_RADIUS_KM*0.621371192, # taken directly from mongodb
|
17
|
-
:ft => EARTH_RADIUS_KM*5280*0.621371192,
|
18
|
-
:sm => EARTH_RADIUS_KM*0.53995680345572 # sea mile
|
13
|
+
:m => EARTH_RADIUS_KM * 1000,
|
14
|
+
:mi => EARTH_RADIUS_KM * 0.621371192, # taken directly from mongodb
|
15
|
+
:ft => EARTH_RADIUS_KM * 5280*0.621371192,
|
16
|
+
:sm => EARTH_RADIUS_KM * 0.53995680345572 # sea mile
|
19
17
|
}
|
20
18
|
|
21
|
-
GEO_FACTORY = RGeo::Geographic.spherical_factory
|
22
|
-
RAD_PER_DEG = Math::PI/180
|
23
19
|
mattr_accessor :lng_symbols
|
24
|
-
@@lng_symbols = LNG_SYMBOLS.dup
|
25
|
-
|
26
20
|
mattr_accessor :lat_symbols
|
27
|
-
@@lat_symbols = LAT_SYMBOLS.dup
|
28
|
-
|
29
21
|
mattr_accessor :earth_radius
|
30
|
-
|
22
|
+
mattr_accessor :factory
|
31
23
|
|
32
|
-
|
33
|
-
@@
|
34
|
-
|
35
|
-
mattr_accessor :default_per_page
|
36
|
-
@@default_per_page = 25
|
37
|
-
|
38
|
-
# mattr_accessor :spherical_distance_formula
|
39
|
-
# @@spherical_distance_formula = :n_vector
|
40
|
-
mattr_accessor :geo_factory
|
41
|
-
@@geo_factory = GEO_FACTORY.dup
|
24
|
+
@@lng_symbols = LNG_SYMBOLS.dup
|
25
|
+
@@lat_symbols = LAT_SYMBOLS.dup
|
26
|
+
@@earth_radius = EARTH_RADIUS.dup
|
42
27
|
|
43
28
|
included do
|
44
29
|
attr_accessor :geo
|
@@ -47,15 +32,25 @@ module Mongoid
|
|
47
32
|
@@spatial_fields_indexed = []
|
48
33
|
end
|
49
34
|
|
35
|
+
def self.use_rgeo
|
36
|
+
require 'mongoid_geospatial/wrappers/rgeo'
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.use_georuby
|
40
|
+
require 'mongoid_geospatial/wrappers/georuby'
|
41
|
+
end
|
42
|
+
|
50
43
|
module ClassMethods #:nodoc:
|
44
|
+
|
51
45
|
# create spatial index for given field
|
52
46
|
# @param [String,Symbol] name
|
53
47
|
# @param [Hash] options options for spatial_index
|
54
|
-
|
48
|
+
# http://www.mongodb.org/display/DOCS/Geospatial+Indexing#GeospatialIndexing-geoNearCommand
|
55
49
|
def spatial_index name, options = {}
|
56
50
|
self.spatial_fields_indexed << name
|
57
51
|
index({name => '2d'}, options)
|
58
52
|
end
|
53
|
+
|
59
54
|
end
|
60
55
|
|
61
56
|
end
|