geomodel 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -1
- data/README.md +24 -6
- data/lib/geomodel.rb +1 -1
- data/lib/geomodel/geocell.rb +15 -14
- data/lib/geomodel/geotypes.rb +1 -1
- data/lib/geomodel/version.rb +1 -1
- data/spec/geocell_spec.rb +44 -0
- data/spec/geotypes_spec.rb +28 -1
- data/spec/spec_helper.rb +3 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 90db919642b9d8269c289645bf18195b8efeb665
|
4
|
+
data.tar.gz: 1278b9035c58e080d225e81aebc38ac7cf1a4b22
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3f471cead361f81bfa870db73eb9ad372f177cf77e6dfb329be0b8fce036e3dc16020f650ae384a7a5a5c9ddeb845058016477512aa95cd368d87fb9b150c9e6
|
7
|
+
data.tar.gz: 17bc0e26b4f43181c3cb22cf398714fb65457bab4ddc495ae4928aba532f095eef9d8b5e6f303252ec5907283bd89ab06885dd6c7c0ffc49739aa90500831862
|
data/CHANGELOG.md
CHANGED
@@ -1,2 +1,5 @@
|
|
1
1
|
### 0.0.1 (January 2, 2014)
|
2
|
-
* Initial release - Straight port from Python, Java and JS implementations
|
2
|
+
* Initial release - Straight port from Python, Java and JS implementations
|
3
|
+
|
4
|
+
### 0.0.2 (January 5, 2014)
|
5
|
+
* Upped the test coverage (which revealed some pretty blatant bugs)
|
data/README.md
CHANGED
@@ -68,10 +68,13 @@ indexed and filtered by either conformance to a bounding box or by proximity
|
|
68
68
|
# Approach
|
69
69
|
|
70
70
|
This Ruby implementation of GeoModel is based on the Python, Java and JavaScript implementations.
|
71
|
-
It's implemented as class level methods contained modules and a few datatype classes. So the
|
72
|
-
part isn't quite there and I don't really see a need for it. Since the library is meant to
|
73
|
-
Non-Relational/Non-ORM environmets, binding the functions/methods to a model does not make
|
71
|
+
It's implemented as class level methods contained within modules and a few datatype classes. So the
|
72
|
+
'model' part isn't quite there and I don't really see a need for it. Since the library is meant to
|
73
|
+
be use in Non-Relational/Non-ORM environmets, binding the functions/methods to a model does not make
|
74
|
+
much sense.
|
75
|
+
|
74
76
|
The model part was mostly implemented in the other libraries to bind directly to Google App Engine.
|
77
|
+
The idea here is to make it backend/db independent and use callbacks to integrate with the backend.
|
75
78
|
|
76
79
|
# References
|
77
80
|
|
@@ -100,7 +103,7 @@ Or install it yourself as:
|
|
100
103
|
Currently, only single-point entities and two types of basic geospatial queries
|
101
104
|
on those entities are supported.
|
102
105
|
|
103
|
-
### Representing your
|
106
|
+
### Representing your Locations
|
104
107
|
|
105
108
|
You'll need a class to hold a geolocation. It assumes that an "entity" has a unique
|
106
109
|
"id" (specific field can be configure), a latitude/longitude combination stored in
|
@@ -125,6 +128,21 @@ flw_spire = Entity.new
|
|
125
128
|
flw_spire.id = 'Flatiron'
|
126
129
|
flw_spire.location = Geomodel::Types::Point.new(33.633406, -111.916803)
|
127
130
|
flw_spire.geocells = Geomodel::GeoCell.generate_geocells(flw_spire.location)
|
131
|
+
|
132
|
+
puts flw_spire.geocells
|
133
|
+
# 8
|
134
|
+
# 8d
|
135
|
+
# 8da
|
136
|
+
# 8daa
|
137
|
+
# 8daab
|
138
|
+
# 8daab6
|
139
|
+
# 8daab66
|
140
|
+
# 8daab666
|
141
|
+
# 8daab6668
|
142
|
+
# 8daab66684
|
143
|
+
# 8daab66684e
|
144
|
+
# 8daab66684e4
|
145
|
+
# 8daab66684e4d
|
128
146
|
```
|
129
147
|
|
130
148
|
### Bounding Box Queries
|
@@ -147,7 +165,7 @@ result_set = my_db.query('SELECT * WHERE location_geocells IN (?)', query_geocel
|
|
147
165
|
matches = Geomodel.filter_result_set_by_bounding_box(bounding_box, result_set)
|
148
166
|
```
|
149
167
|
|
150
|
-
###
|
168
|
+
### Proximity (nearest-n) Queries
|
151
169
|
|
152
170
|
Find nearby locations given a location (lat & lon) and a radius in meters:
|
153
171
|
|
@@ -189,4 +207,4 @@ distances = results.map(&:last)
|
|
189
207
|
|
190
208
|
## License
|
191
209
|
|
192
|
-
MIT License
|
210
|
+
MIT License
|
data/lib/geomodel.rb
CHANGED
@@ -84,7 +84,7 @@ module Geomodel
|
|
84
84
|
while !cur_geocells.empty?
|
85
85
|
closest_possible_next_result_dist = sorted_edge_distances[0]
|
86
86
|
|
87
|
-
next if max_distance
|
87
|
+
next if max_distance && closest_possible_next_result_dist > max_distance
|
88
88
|
|
89
89
|
cur_geocells_unique = cur_geocells - searched_cells.to_a
|
90
90
|
|
data/lib/geomodel/geocell.rb
CHANGED
@@ -267,7 +267,7 @@ module Geomodel
|
|
267
267
|
cell_adj_arr = cell.split(//) # Split the geocell string characters into a list.
|
268
268
|
i = cell_adj_arr.size - 1
|
269
269
|
|
270
|
-
while i >= 0 && (dx != 0
|
270
|
+
while i >= 0 && (dx != 0 || dy != 0)
|
271
271
|
x, y = subdiv_xy(cell_adj_arr[i])
|
272
272
|
|
273
273
|
# Horizontal adjacency.
|
@@ -339,24 +339,25 @@ module Geomodel
|
|
339
339
|
if between_w_e
|
340
340
|
if between_n_s
|
341
341
|
# Inside the geocell.
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
342
|
+
|
343
|
+
return [Geomodel::Math.distance(point, Geomodel::Types::Point.new(bbox.south, point.lon)),
|
344
|
+
Geomodel::Math.distance(point, Geomodel::Types::Point.new(bbox.north, point.lon)),
|
345
|
+
Geomodel::Math.distance(point, Geomodel::Types::Point.new(point.lat, bbox.east)),
|
346
|
+
Geomodel::Math.distance(point, Geomodel::Types::Point.new(point.lat, bbox.west))].min
|
346
347
|
else
|
347
|
-
return [
|
348
|
-
|
348
|
+
return [Geomodel::Math.distance(point, Geomodel::Types::Point.new(bbox.south, point.lon)),
|
349
|
+
Geomodel::Math.distance(point, Geomodel::Types::Point.new(bbox.north, point.lon))].min
|
349
350
|
end
|
350
351
|
else
|
351
352
|
if between_n_s
|
352
|
-
return [
|
353
|
-
|
353
|
+
return [Geomodel::Math.distance(point, Geomodel::Types::Point.new(point.lat, bbox.east)),
|
354
|
+
Geomodel::Math.distance(point, Geomodel::Types::Point.new(point.lat, bbox.west))].min
|
354
355
|
else
|
355
356
|
# TODO(romannurik): optimize
|
356
|
-
return [
|
357
|
-
|
358
|
-
|
359
|
-
|
357
|
+
return [Geomodel::Math.distance(point, Geomodel::Types::Point.new(bbox.south, bbox.east)),
|
358
|
+
Geomodel::Math.distance(point, Geomodel::Types::Point.new(bbox.north, bbox.east)),
|
359
|
+
Geomodel::Math.distance(point, Geomodel::Types::Point.new(bbox.south, bbox.west)),
|
360
|
+
Geomodel::Math.distance(point, Geomodel::Types::Point.new(bbox.north, bbox.west))].min
|
360
361
|
end
|
361
362
|
end
|
362
363
|
end
|
@@ -444,7 +445,7 @@ module Geomodel
|
|
444
445
|
# For example, the immediate children of 'a' are 'a0', 'a1', ..., 'af'.
|
445
446
|
#
|
446
447
|
def self.children(cell)
|
447
|
-
GEOCELL_ALPHABET.map { |chr| cell + chr }
|
448
|
+
GEOCELL_ALPHABET.split(//).map { |chr| cell + chr }
|
448
449
|
end
|
449
450
|
|
450
451
|
# Returns the (x, y) of the geocell character in the 4x4 alphabet grid.
|
data/lib/geomodel/geotypes.rb
CHANGED
data/lib/geomodel/version.rb
CHANGED
data/spec/geocell_spec.rb
CHANGED
@@ -58,6 +58,26 @@ describe 'Geomodel::GeoCell' do
|
|
58
58
|
expect(all_adjacents.size).to eq(8)
|
59
59
|
end
|
60
60
|
|
61
|
+
it "can determine adjacency left and bottom of parent cell" do
|
62
|
+
cells = {
|
63
|
+
"8e6187fe6187fa" => ["8e6187fe618d45", "8e6187fe618d50", "8e6187fe618d51", "8e6187fe6187fb", "8e6187fe6187f9", "8e6187fe6187f8", "8e6187fe6187ed", "8e6187fe6187ef"],
|
64
|
+
"8e6187fe618d45" => ["8e6187fe618d46", "8e6187fe618d47", "8e6187fe618d52", "8e6187fe618d50", "8e6187fe6187fa", "8e6187fe6187ef", "8e6187fe6187ee", "8e6187fe618d44"]
|
65
|
+
}
|
66
|
+
cells.each do |cell, adjacents|
|
67
|
+
expect(Geomodel::GeoCell.all_adjacents(cell)).to eq(adjacents)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
it "cam calculate the immediate children of a given geocell" do
|
72
|
+
expect(Geomodel::GeoCell.children("8e6187fe6187f")).
|
73
|
+
to eq(
|
74
|
+
%w(8e6187fe6187f0 8e6187fe6187f1 8e6187fe6187f2 8e6187fe6187f3
|
75
|
+
8e6187fe6187f4 8e6187fe6187f5 8e6187fe6187f6 8e6187fe6187f7
|
76
|
+
8e6187fe6187f8 8e6187fe6187f9 8e6187fe6187fa 8e6187fe6187fb
|
77
|
+
8e6187fe6187fc 8e6187fe6187fd 8e6187fe6187fe 8e6187fe6187ff)
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
61
81
|
it "can determine collinearity" do
|
62
82
|
cell = Geomodel::GeoCell.compute(Geomodel::Types::Point.new(37, -122), 14)
|
63
83
|
|
@@ -109,6 +129,30 @@ describe 'Geomodel::GeoCell' do
|
|
109
129
|
expect(geocells).to include("9aa228a8b3b00")
|
110
130
|
end
|
111
131
|
|
132
|
+
it "can calculate that the shortest distance between a point and a geocell bounding boxfor the point is effectively zero" do
|
133
|
+
point = Geomodel::Types::Point.new(40.7407092, -73.9894039)
|
134
|
+
cell = "9ac7be064ea77"
|
135
|
+
expect(Geomodel::GeoCell.point_distance(cell, point)).to be_within(0.2).of(0.0)
|
136
|
+
end
|
137
|
+
|
138
|
+
it "can calculate the shortest distance between a point outside a geocell and the geocell" do
|
139
|
+
point = Geomodel::Types::Point.new(40.7425610, -73.9922670)
|
140
|
+
cell = "9ac7be064ea77"
|
141
|
+
expect(Geomodel::GeoCell.point_distance(cell, point)).to be_within(0.2).of(317.2)
|
142
|
+
end
|
143
|
+
|
144
|
+
it "can calculate the shortest distance between a point between north and south (but not between east and west) of a geocell bounding box" do
|
145
|
+
point = Geomodel::Types::Point.new(40.740710, -74.025537)
|
146
|
+
cell = "9ac7be064ea77"
|
147
|
+
expect(Geomodel::GeoCell.point_distance(cell, point)).to be_within(0.2).of(3047.3)
|
148
|
+
end
|
149
|
+
|
150
|
+
it "can calculate the shortest distance between a point between east and west (but not between north and south) of a geocell bounding box" do
|
151
|
+
point = Geomodel::Types::Point.new(40.740720, -73.989403)
|
152
|
+
cell = "9ac7be064ea77"
|
153
|
+
expect(Geomodel::GeoCell.point_distance(cell, point)).to be_within(0.2).of(0.99)
|
154
|
+
end
|
155
|
+
|
112
156
|
# TODO implement these tests!
|
113
157
|
|
114
158
|
# @Test
|
data/spec/geotypes_spec.rb
CHANGED
@@ -54,12 +54,39 @@ describe 'Geomodel::Types' do
|
|
54
54
|
expect(box_a).to eq(box_b)
|
55
55
|
end
|
56
56
|
|
57
|
-
it "can be created with north below south" do
|
57
|
+
it "can't be created with north below south" do
|
58
58
|
box = Geomodel::Types::Box.new(37, -122, 34, -125)
|
59
59
|
|
60
60
|
expect { box.north = 32 }.to raise_error
|
61
61
|
expect { box.south = 39 }.to raise_error
|
62
62
|
end
|
63
|
+
|
64
|
+
it "can be created with south below north" do
|
65
|
+
box = Geomodel::Types::Box.new(37, -122, 34, -125)
|
66
|
+
|
67
|
+
expect { box.north = 39 }.to_not raise_error
|
68
|
+
expect { box.south = 32 }.to_not raise_error
|
69
|
+
end
|
70
|
+
|
71
|
+
it "can be set the values for north, east, south and west" do
|
72
|
+
box = Geomodel::Types::Box.new(37, -122, 34, -125)
|
73
|
+
|
74
|
+
expect { box.north = 39 }.to_not raise_error
|
75
|
+
expect { box.south = 32 }.to_not raise_error
|
76
|
+
expect { box.east = -123 }.to_not raise_error
|
77
|
+
expect { box.west = -126 }.to_not raise_error
|
78
|
+
|
79
|
+
expect(box.north).to eq(39)
|
80
|
+
expect(box.south).to eq(32)
|
81
|
+
expect(box.east).to eq(-123)
|
82
|
+
expect(box.west).to eq(-126)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "returns a suitable string representation" do
|
86
|
+
box = Geomodel::Types::Box.new(37, -122, 34, -125)
|
87
|
+
|
88
|
+
expect(box.to_s).to eq('(37, -122, 34, -125)')
|
89
|
+
end
|
63
90
|
|
64
91
|
end
|
65
92
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: geomodel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Sam-Bodden
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-01-
|
11
|
+
date: 2014-01-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: geocoder
|