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.
@@ -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