geos-extensions 0.1.6 → 0.2.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/VERSION +1 -1
- data/geos-extensions.gemspec +10 -6
- data/lib/geos/active_record_extensions/connection_adapters/postgresql_adapter.rb +115 -11
- data/lib/geos/active_record_extensions/spatial_columns.rb +369 -0
- data/lib/geos/active_record_extensions/{geospatial_scopes.rb → spatial_scopes.rb} +98 -21
- data/lib/geos/active_record_extensions.rb +4 -2
- data/test/adapter_tests.rb +15 -0
- data/test/fixtures/foo3ds.yml +16 -0
- data/test/fixtures/foo_geographies.yml +16 -0
- data/test/geography_columns_tests.rb +176 -0
- data/test/geometry_columns_tests.rb +2 -2
- data/test/spatial_scopes_geographies_tests.rb +107 -0
- data/test/{geospatial_scopes_tests.rb → spatial_scopes_tests.rb} +53 -13
- data/test/test_helper.rb +33 -7
- metadata +10 -6
- data/lib/geos/active_record_extensions/geometry_columns.rb +0 -264
@@ -87,18 +87,24 @@ module Geos
|
|
87
87
|
#
|
88
88
|
# == SRID Detection
|
89
89
|
#
|
90
|
+
# * the default SRID according to the SQL-MM standard is 0, but versions
|
91
|
+
# of PostGIS prior to 2.0 would return -1. We do some detection here
|
92
|
+
# and set the value of Geos::ActiveRecord.UNKNOWN_SRIDS[type]
|
93
|
+
# accordingly.
|
90
94
|
# * if the geometry itself has an SRID, we'll compare it to the
|
91
95
|
# geometry of the column. If they differ, we'll use ST_Transform
|
92
96
|
# to transform the geometry to the proper SRID for comparison. If
|
93
97
|
# they're the same, no conversion is necessary.
|
94
98
|
# * if no SRID is specified in the geometry, we'll use ST_SetSRID
|
95
99
|
# to set the SRID to the column's SRID.
|
96
|
-
# * in cases where the column has been defined with an SRID of
|
97
|
-
#
|
98
|
-
#
|
99
|
-
# as we'll assume the SRID of the column to be whatever
|
100
|
-
# the geometry is.
|
101
|
-
|
100
|
+
# * in cases where the column has been defined with an SRID of
|
101
|
+
# UNKNOWN_SRIDS[type], no transformation is done, but we'll set the SRID
|
102
|
+
# of the geometry to UNKNOWN_SRIDS[type] to perform the query using
|
103
|
+
# ST_SetSRID, as we'll assume the SRID of the column to be whatever
|
104
|
+
# the SRID of the geometry is.
|
105
|
+
# * when using geography types, the SRID is never transformed since
|
106
|
+
# it's assumed that all of your geometries will be in 4326.
|
107
|
+
module SpatialScopes
|
102
108
|
SCOPE_METHOD = if ::ActiveRecord::VERSION::MAJOR >= 3
|
103
109
|
'scope'
|
104
110
|
else
|
@@ -118,7 +124,7 @@ module Geos
|
|
118
124
|
overlaps
|
119
125
|
touches
|
120
126
|
within
|
121
|
-
}
|
127
|
+
}
|
122
128
|
|
123
129
|
ZERO_ARGUMENT_MEASUREMENTS = %w{
|
124
130
|
area
|
@@ -129,12 +135,10 @@ module Geos
|
|
129
135
|
numinteriorring
|
130
136
|
numinteriorrings
|
131
137
|
numpoints
|
132
|
-
length3d
|
133
138
|
length
|
134
139
|
length2d
|
135
140
|
perimeter
|
136
141
|
perimeter2d
|
137
|
-
perimeter3d
|
138
142
|
}
|
139
143
|
|
140
144
|
ONE_GEOMETRY_ARGUMENT_MEASUREMENTS = %w{
|
@@ -150,23 +154,85 @@ module Geos
|
|
150
154
|
|
151
155
|
ONE_ARGUMENT_MEASUREMENTS = %w{
|
152
156
|
length2d_spheroid
|
153
|
-
length3d_spheroid
|
154
157
|
length_spheroid
|
155
158
|
}
|
156
159
|
|
160
|
+
# Some functions were renamed in PostGIS 2.0.
|
161
|
+
if Geos::ActiveRecord.POSTGIS[:lib] >= '2.0'
|
162
|
+
RELATIONSHIPS.concat(%w{
|
163
|
+
3dintersects
|
164
|
+
})
|
165
|
+
|
166
|
+
ZERO_ARGUMENT_MEASUREMENTS.concat(%w{
|
167
|
+
3dlength
|
168
|
+
3dperimeter
|
169
|
+
})
|
170
|
+
|
171
|
+
ONE_ARGUMENT_MEASUREMENTS.concat(%w{
|
172
|
+
3dlength_spheroid
|
173
|
+
})
|
174
|
+
|
175
|
+
ONE_GEOMETRY_ARGUMENT_MEASUREMENTS.concat(%w{
|
176
|
+
3ddistance
|
177
|
+
3dmaxdistance
|
178
|
+
})
|
179
|
+
|
180
|
+
ONE_GEOMETRY_ARGUMENT_AND_ONE_ARGUMENT_RELATIONSHIPS.concat(%w{
|
181
|
+
3ddwithin
|
182
|
+
3ddfullywithin
|
183
|
+
})
|
184
|
+
else
|
185
|
+
ZERO_ARGUMENT_MEASUREMENTS.concat(%w{
|
186
|
+
length3d
|
187
|
+
perimeter3d
|
188
|
+
})
|
189
|
+
|
190
|
+
ONE_ARGUMENT_MEASUREMENTS.concat(%w{
|
191
|
+
length3d_spheroid
|
192
|
+
})
|
193
|
+
end
|
194
|
+
|
195
|
+
FUNCTION_ALIASES = {
|
196
|
+
'order_by_max_distance' => 'order_by_maxdistance',
|
197
|
+
'st_geometrytype' => 'st_geometry_type'
|
198
|
+
}
|
199
|
+
|
200
|
+
COMPATIBILITY_FUNCTION_ALIASES = if Geos::ActiveRecord.POSTGIS[:lib] >= '2.0'
|
201
|
+
{
|
202
|
+
'order_by_length3d' => 'order_by_3dlength',
|
203
|
+
'order_by_perimeter3d' => 'order_by_3dperimeter',
|
204
|
+
'order_by_length3d_spheroid' => 'order_by_3dlength_spheroid'
|
205
|
+
}
|
206
|
+
else
|
207
|
+
{
|
208
|
+
'order_by_3dlength' => 'order_by_length3d',
|
209
|
+
'order_by_3dperimeter' => 'order_by_perimeter3d',
|
210
|
+
'order_by_3dlength_spheroid' => 'order_by_length3d_spheroid'
|
211
|
+
}
|
212
|
+
end
|
213
|
+
|
214
|
+
if Geos::ActiveRecord.POSTGIS[:lib] >= '2.0'
|
215
|
+
FUNCTION_ALIASES.merge!({
|
216
|
+
'st_3d_dwithin' => 'st_3ddwithin',
|
217
|
+
'st_3d_dfully_within' => 'st_3ddfullywithin',
|
218
|
+
'order_by_3d_distance' => 'order_by_3ddistance',
|
219
|
+
'order_by_3d_max_distance' => 'order_by_3dmaxdistance'
|
220
|
+
})
|
221
|
+
end
|
222
|
+
|
157
223
|
def self.included(base)
|
158
224
|
base.class_eval do
|
159
225
|
class << self
|
160
226
|
protected
|
161
|
-
def set_srid_or_transform(column_srid, geom_srid, geos)
|
162
|
-
sql = if column_srid != geom_srid
|
163
|
-
if column_srid ==
|
164
|
-
%{ST_SetSRID(
|
227
|
+
def set_srid_or_transform(column_srid, geom_srid, geos, type)
|
228
|
+
sql = if type != :geography && column_srid != geom_srid
|
229
|
+
if column_srid == Geos::ActiveRecord.UNKNOWN_SRIDS[type] || geom_srid == Geos::ActiveRecord.UNKNOWN_SRIDS[type]
|
230
|
+
%{ST_SetSRID(?::#{type}, #{column_srid})}
|
165
231
|
else
|
166
|
-
%{ST_Transform(
|
232
|
+
%{ST_Transform(?::#{type}, #{column_srid})}
|
167
233
|
end
|
168
234
|
else
|
169
|
-
%{
|
235
|
+
%{?::#{type}}
|
170
236
|
end
|
171
237
|
|
172
238
|
sanitize_sql([ sql, geos.to_ewkb ])
|
@@ -179,9 +245,9 @@ module Geos
|
|
179
245
|
Geos.read(geom)
|
180
246
|
end
|
181
247
|
|
182
|
-
def read_geom_srid(geos)
|
183
|
-
if geos.srid == 0
|
184
|
-
|
248
|
+
def read_geom_srid(geos, column_type = :geometry)
|
249
|
+
if geos.srid == 0 || geos.srid == -1
|
250
|
+
Geos::ActiveRecord.UNKNOWN_SRIDS[column_type]
|
185
251
|
else
|
186
252
|
geos.srid
|
187
253
|
end
|
@@ -223,12 +289,13 @@ module Geos
|
|
223
289
|
ret << "#{function_name(function, options[:use_index])}(#{self.quoted_table_name}.#{column_name}"
|
224
290
|
|
225
291
|
if geom
|
292
|
+
column_type = self.spatial_column_by_name(options[:column]).spatial_type
|
226
293
|
column_srid = self.srid_for(options[:column])
|
227
294
|
|
228
295
|
geos = read_geos(geom, column_srid)
|
229
|
-
geom_srid = read_geom_srid(geos)
|
296
|
+
geom_srid = read_geom_srid(geos, column_type)
|
230
297
|
|
231
|
-
ret << %{, #{self.set_srid_or_transform(column_srid, geom_srid, geos)}}
|
298
|
+
ret << %{, #{self.set_srid_or_transform(column_srid, geom_srid, geos, column_type)}}
|
232
299
|
end
|
233
300
|
|
234
301
|
ret << ', ?' * function_options[:additional_args]
|
@@ -407,8 +474,18 @@ module Geos
|
|
407
474
|
])
|
408
475
|
}
|
409
476
|
})
|
477
|
+
|
478
|
+
class << base
|
479
|
+
aliases = COMPATIBILITY_FUNCTION_ALIASES.merge(FUNCTION_ALIASES)
|
480
|
+
|
481
|
+
aliases.each do |k, v|
|
482
|
+
alias_method(k, v)
|
483
|
+
end
|
484
|
+
end
|
410
485
|
end
|
411
486
|
end
|
412
487
|
end
|
488
|
+
|
489
|
+
GeospatialScopes = SpatialScopes
|
413
490
|
end
|
414
491
|
end
|
@@ -3,8 +3,10 @@ require File.join(Geos::GEOS_EXTENSIONS_BASE, *%w{ geos active_record_extensions
|
|
3
3
|
|
4
4
|
module Geos
|
5
5
|
module ActiveRecord
|
6
|
-
autoload :
|
7
|
-
autoload :
|
6
|
+
autoload :SpatialColumns, File.join(Geos::GEOS_EXTENSIONS_BASE, %w{ geos active_record_extensions spatial_columns })
|
7
|
+
autoload :GeometryColumns, File.join(Geos::GEOS_EXTENSIONS_BASE, %w{ geos active_record_extensions spatial_columns })
|
8
|
+
autoload :SpatialScopes, File.join(Geos::GEOS_EXTENSIONS_BASE, %w{ geos active_record_extensions spatial_scopes })
|
9
|
+
autoload :GeospatialScopes, File.join(Geos::GEOS_EXTENSIONS_BASE, %w{ geos active_record_extensions spatial_scopes })
|
8
10
|
end
|
9
11
|
end
|
10
12
|
|
data/test/adapter_tests.rb
CHANGED
@@ -19,5 +19,20 @@ if ENV['TEST_ACTIVERECORD']
|
|
19
19
|
assert_equal(2, geometry_columns.length)
|
20
20
|
assert_equal(2, other_columns.length)
|
21
21
|
end
|
22
|
+
|
23
|
+
if Geos::ActiveRecord.geography_columns?
|
24
|
+
def test_simplified_type_geography
|
25
|
+
geography_columns = FooGeography.columns.select do |c|
|
26
|
+
c.type == :geography
|
27
|
+
end
|
28
|
+
|
29
|
+
other_columns = FooGeography.columns.select do |c|
|
30
|
+
c.type != :geography
|
31
|
+
end
|
32
|
+
|
33
|
+
assert_equal(2, geography_columns.length)
|
34
|
+
assert_equal(2, other_columns.length)
|
35
|
+
end
|
36
|
+
end
|
22
37
|
end
|
23
38
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
---
|
2
|
+
one:
|
3
|
+
id: 1
|
4
|
+
name: "one"
|
5
|
+
the_geom: 'POINT(0 0 0)'
|
6
|
+
the_other_geom: 'SRID=4326; POINT(10 10 10)'
|
7
|
+
two:
|
8
|
+
id: 2
|
9
|
+
name: "two"
|
10
|
+
the_geom: 'POINT(10 10 10)'
|
11
|
+
the_other_geom: 'SRID=4326; POINT(20 20 20)'
|
12
|
+
three:
|
13
|
+
id: 3
|
14
|
+
name: "three"
|
15
|
+
the_geom: 'POLYGON((-5 -5 -5, -5 5 5, 5 5 5, 5 -5 -5, -5 -5 -5))'
|
16
|
+
the_other_geom: 'SRID=4326; POLYGON((5 5 5, 5 10 10, 10 10 10, 10 5 5, 5 5 5))'
|
@@ -0,0 +1,16 @@
|
|
1
|
+
---
|
2
|
+
one:
|
3
|
+
id: 1
|
4
|
+
name: "one"
|
5
|
+
the_geom: 'POINT(0 0)'
|
6
|
+
the_other_geom: 'SRID=4326; POINT(10 10)'
|
7
|
+
two:
|
8
|
+
id: 2
|
9
|
+
name: "two"
|
10
|
+
the_geom: 'POINT(10 10)'
|
11
|
+
the_other_geom: 'SRID=4326; POINT(20 20)'
|
12
|
+
three:
|
13
|
+
id: 3
|
14
|
+
name: "three"
|
15
|
+
the_geom: 'POLYGON((-5 -5, -5 5, 5 5, 5 -5, -5 -5))'
|
16
|
+
the_other_geom: 'SRID=4326; POLYGON((5 5, 5 10, 10 10, 10 5, 5 5))'
|
@@ -0,0 +1,176 @@
|
|
1
|
+
|
2
|
+
$: << File.dirname(__FILE__)
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
if ENV['TEST_ACTIVERECORD']
|
6
|
+
class GeographyColumnsTests < ActiveRecord::TestCase
|
7
|
+
include TestHelper
|
8
|
+
include ActiveRecord::TestFixtures
|
9
|
+
|
10
|
+
self.fixture_path = File.join(File.dirname(__FILE__), 'fixtures')
|
11
|
+
fixtures :foo_geographies
|
12
|
+
|
13
|
+
def test_geography_columns_detected
|
14
|
+
assert_equal(2, FooGeography.geography_columns.length)
|
15
|
+
|
16
|
+
FooGeography.geography_columns.each do |column|
|
17
|
+
assert_kind_of(ActiveRecord::ConnectionAdapters::PostgreSQLSpatialColumn, column)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_srid_for
|
22
|
+
assert_equal(0, FooGeography.srid_for(:the_geom))
|
23
|
+
assert_equal(4326, FooGeography.srid_for(:the_other_geom))
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_coord_dimension_for
|
27
|
+
assert_equal(2, FooGeography.coord_dimension_for(:the_geom))
|
28
|
+
assert_equal(2, FooGeography.coord_dimension_for(:the_other_geom))
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_geography_column_by_name
|
32
|
+
assert_kind_of(ActiveRecord::ConnectionAdapters::PostgreSQLSpatialColumn, FooGeography.geography_column_by_name(:the_geom))
|
33
|
+
assert_kind_of(ActiveRecord::ConnectionAdapters::PostgreSQLSpatialColumn, FooGeography.geography_column_by_name(:the_other_geom))
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_accessors
|
37
|
+
foo = FooGeography.find(1)
|
38
|
+
|
39
|
+
Geos::ActiveRecord::SpatialColumns::SPATIAL_COLUMN_OUTPUT_FORMATS.each do |format|
|
40
|
+
assert(foo.respond_to?("the_geom_#{format}"))
|
41
|
+
assert(foo.respond_to?("the_other_geom_#{format}"))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_without_accessor
|
46
|
+
foo = FooGeography.find(1)
|
47
|
+
assert_kind_of(String, foo.the_geom)
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_geos_accessor
|
51
|
+
foo = FooGeography.find(1)
|
52
|
+
assert_kind_of(Geos::Point, foo.the_geom_geos)
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_wkt_accessor
|
56
|
+
foo = FooGeography.find(1)
|
57
|
+
assert_kind_of(String, foo.the_geom_wkt)
|
58
|
+
assert_match(/^POINT\s*\(0\.0+\s+0\.0+\)$/, foo.the_geom_wkt)
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_wkb_accessor
|
62
|
+
foo = FooGeography.find(1)
|
63
|
+
assert_kind_of(String, foo.the_geom_wkb)
|
64
|
+
assert_match(/^[A-F0-9]+$/, foo.the_geom_wkb)
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_ewkt_accessor
|
68
|
+
foo = FooGeography.find(1)
|
69
|
+
assert_kind_of(String, foo.the_geom_ewkt)
|
70
|
+
assert_match(/^SRID=\d+;POINT\s*\(0\.0+\s+0\.0+\)$/, foo.the_geom_ewkt)
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_ewkb_accessor
|
74
|
+
foo = FooGeography.find(1)
|
75
|
+
assert_kind_of(String, foo.the_geom_ewkb)
|
76
|
+
assert(/^[A-F0-9]+$/, foo.the_geom_wkb)
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_wkb_bin_accessor
|
80
|
+
foo = FooGeography.find(1)
|
81
|
+
assert_kind_of(String, foo.the_geom_wkb_bin)
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_ewkb_bin_accessor
|
85
|
+
foo = FooGeography.find(1)
|
86
|
+
assert_kind_of(String, foo.the_geom_ewkb_bin)
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_geos_create
|
90
|
+
foo = FooGeography.create!(
|
91
|
+
:name => 'test_geos_create',
|
92
|
+
:the_geom => Geos.read(POINT_WKT)
|
93
|
+
)
|
94
|
+
|
95
|
+
foo.reload
|
96
|
+
assert_saneness_of_point(foo.the_geom_geos)
|
97
|
+
end
|
98
|
+
|
99
|
+
def test_wkt_create
|
100
|
+
foo = FooGeography.create!(
|
101
|
+
:name => 'test_wkt_create',
|
102
|
+
:the_geom => POINT_WKT
|
103
|
+
)
|
104
|
+
|
105
|
+
foo.reload
|
106
|
+
assert_saneness_of_point(foo.the_geom_geos)
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_wkb_create
|
110
|
+
foo = FooGeography.create!(
|
111
|
+
:name => 'test_wkb_create',
|
112
|
+
:the_geom => POINT_WKB
|
113
|
+
)
|
114
|
+
|
115
|
+
foo.reload
|
116
|
+
assert_saneness_of_point(foo.the_geom_geos)
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_ewkt_create_with_srid_4326
|
120
|
+
foo = FooGeography.create!(
|
121
|
+
:name => 'test_ewkt_create_with_srid_4326',
|
122
|
+
:the_other_geom => POINT_EWKT
|
123
|
+
)
|
124
|
+
|
125
|
+
foo.reload
|
126
|
+
assert_saneness_of_point(foo.the_other_geom_geos)
|
127
|
+
end
|
128
|
+
|
129
|
+
def test_create_with_no_srid_converting_to_4326
|
130
|
+
foo = FooGeography.create!(
|
131
|
+
:name => 'test_ewkt_create_with_no_srid_converting_to_4326',
|
132
|
+
:the_other_geom => POINT_WKT
|
133
|
+
)
|
134
|
+
|
135
|
+
foo.reload
|
136
|
+
assert_saneness_of_point(foo.the_other_geom_geos)
|
137
|
+
end
|
138
|
+
|
139
|
+
def test_create_with_no_srid_converting_to_minus_1
|
140
|
+
foo = FooGeography.create!(
|
141
|
+
:name => 'test_ewkt_create_with_no_srid_converting_to_minus_1',
|
142
|
+
:the_geom => POINT_EWKT
|
143
|
+
)
|
144
|
+
|
145
|
+
foo.reload
|
146
|
+
assert_saneness_of_point(foo.the_geom_geos)
|
147
|
+
end
|
148
|
+
|
149
|
+
def test_create_with_converting_from_900913_to_4326
|
150
|
+
foo = FooGeography.create!(
|
151
|
+
:name => 'test_create_with_converting_from_900913_to_4326',
|
152
|
+
:the_other_geom => "SRID=900913; #{POINT_WKT}"
|
153
|
+
)
|
154
|
+
end
|
155
|
+
|
156
|
+
def test_ewkt_create_with_srid_default
|
157
|
+
foo = FooGeography.create!(
|
158
|
+
:name => 'test_ewkt_create_with_srid_default',
|
159
|
+
:the_other_geom => POINT_EWKT_WITH_DEFAULT
|
160
|
+
)
|
161
|
+
|
162
|
+
foo.reload
|
163
|
+
assert_saneness_of_point(foo.the_other_geom_geos)
|
164
|
+
end
|
165
|
+
|
166
|
+
def test_ewkb_create
|
167
|
+
foo = FooGeography.create!(
|
168
|
+
:name => 'test_ewkb_create',
|
169
|
+
:the_other_geom => POINT_EWKB
|
170
|
+
)
|
171
|
+
|
172
|
+
foo.reload
|
173
|
+
assert_saneness_of_point(foo.the_other_geom_geos)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
@@ -19,7 +19,7 @@ if ENV['TEST_ACTIVERECORD']
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def test_srid_for
|
22
|
-
assert_equal(
|
22
|
+
assert_equal(Geos::ActiveRecord.UNKNOWN_SRID, Foo.srid_for(:the_geom))
|
23
23
|
assert_equal(4326, Foo.srid_for(:the_other_geom))
|
24
24
|
end
|
25
25
|
|
@@ -36,7 +36,7 @@ if ENV['TEST_ACTIVERECORD']
|
|
36
36
|
def test_accessors
|
37
37
|
foo = Foo.find(1)
|
38
38
|
|
39
|
-
Geos::ActiveRecord::
|
39
|
+
Geos::ActiveRecord::SpatialColumns::SPATIAL_COLUMN_OUTPUT_FORMATS.each do |format|
|
40
40
|
assert(foo.respond_to?("the_geom_#{format}"))
|
41
41
|
assert(foo.respond_to?("the_other_geom_#{format}"))
|
42
42
|
end
|