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.
@@ -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 -1
97
- # (PostGIS's default), no transformation is done, but we'll set the
98
- # SRID of the geometry to -1 to perform the query using ST_SetSRID,
99
- # as we'll assume the SRID of the column to be whatever the SRID of
100
- # the geometry is.
101
- module GeospatialScopes
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
- }.freeze
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 == -1 || geom_srid == -1
164
- %{ST_SetSRID(?, #{column_srid})}
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(?, #{column_srid})}
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
- -1
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 :GeometryColumns, File.join(Geos::GEOS_EXTENSIONS_BASE, %w{ geos active_record_extensions geometry_columns })
7
- autoload :GeospatialScopes, File.join(Geos::GEOS_EXTENSIONS_BASE, %w{ geos active_record_extensions geospatial_scopes })
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
 
@@ -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(-1, Foo.srid_for(:the_geom))
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::GeometryColumns::GEOMETRY_COLUMN_OUTPUT_FORMATS.each do |format|
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