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