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
@@ -0,0 +1,107 @@
|
|
1
|
+
|
2
|
+
$: << File.dirname(__FILE__)
|
3
|
+
require 'test_helper'
|
4
|
+
|
5
|
+
if ENV['TEST_ACTIVERECORD']
|
6
|
+
class SpatialScopesGeographiesTests < 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 ids_tester(method, args, ids = [], options = {})
|
14
|
+
geoms = FooGeography.send(method, *Array(args)).all(options)
|
15
|
+
assert_equal(ids.sort, geoms.collect(&:id).sort)
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_covers
|
19
|
+
ids_tester(:st_covers, 'POINT(0 0)', [ 3 ], :conditions => {
|
20
|
+
:id => [ 3 ]
|
21
|
+
})
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_coveredby
|
25
|
+
ids_tester(:st_coveredby, 'POLYGON((-6 -6, -6 6, 6 6, 6 -6, -6 -6))', [ 1 ], :conditions => {
|
26
|
+
:id => [ 1, 2 ]
|
27
|
+
})
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_intersects
|
31
|
+
ids_tester(:st_intersects, 'LINESTRING(-5 -5, 10 10)', [ 2, 3 ])
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_dwithin
|
35
|
+
ids_tester(:st_dwithin, [ 'POINT(5 5)', 10 ], [ 3 ])
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_allow_null
|
39
|
+
begin
|
40
|
+
foo = FooGeography.create(:name => 'four')
|
41
|
+
ids_tester(:st_covers, [ 'POINT(3 3)', { :allow_null => true } ], [ 3, foo.id ])
|
42
|
+
ensure
|
43
|
+
FooGeography.find_by_name('four').destroy
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_with_column
|
48
|
+
assert_equal([3], FooGeography.st_covers('POINT(7 7)', :column => :the_other_geom).all.collect(&:id).sort)
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_with_srid_switching
|
52
|
+
assert_equal([3], FooGeography.st_covers('SRID=4326; POINT(3 3)').all.collect(&:id).sort)
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_with_srid_default
|
56
|
+
assert_equal([3], FooGeography.st_covers('SRID=default; POINT(3 3)').all.collect(&:id).sort)
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_with_srid_transform
|
60
|
+
assert_equal([3], FooGeography.st_covers('SRID=4269; POINT(7 7)', :column => :the_other_geom).all.collect(&:id).sort)
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_order_by_distance
|
64
|
+
assert_equal([3, 1, 2], FooGeography.order_by_distance('POINT(1 1)').all.collect(&:id))
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_order_by_distance_desc
|
68
|
+
assert_equal([2, 1, 3], FooGeography.order_by_distance('POINT(1 1)', :desc => true).all.collect(&:id))
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_order_by_area
|
72
|
+
assert_equal([1, 2, 3], FooGeography.order_by_area.order('id').all.collect(&:id))
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_order_by_area_desc
|
76
|
+
assert_equal([3, 1, 2], FooGeography.order_by_area(:desc => true).order('id').all.collect(&:id))
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_order_by_length
|
80
|
+
assert_equal([1, 2, 3], FooGeography.order_by_length.order('id').all.collect(&:id))
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_order_by_length_desc
|
84
|
+
expected = if Geos::ActiveRecord.POSTGIS[:lib] >= '2.0'
|
85
|
+
[1, 2, 3]
|
86
|
+
else
|
87
|
+
[3, 1, 2]
|
88
|
+
end
|
89
|
+
|
90
|
+
assert_equal(expected, FooGeography.order_by_length(:desc => true).order('id').where('true = true').all.collect(&:id))
|
91
|
+
end
|
92
|
+
|
93
|
+
if Geos::ActiveRecord.POSTGIS[:lib] >= '2.0'
|
94
|
+
def test_order_by_perimeter
|
95
|
+
assert_equal([1, 2, 3], FooGeography.order_by_perimeter.order('id').all.collect(&:id))
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_order_by_perimeter_desc
|
99
|
+
assert_equal([3, 1, 2], FooGeography.order_by_perimeter(:desc => true).order('id').all.collect(&:id))
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_order_by_area_with_desc_symbol
|
104
|
+
assert_equal([3, 1, 2], FooGeography.order_by_area(:desc).order('id').all.collect(&:id))
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -3,15 +3,15 @@ $: << File.dirname(__FILE__)
|
|
3
3
|
require 'test_helper'
|
4
4
|
|
5
5
|
if ENV['TEST_ACTIVERECORD']
|
6
|
-
class
|
6
|
+
class SpatialScopesTests < ActiveRecord::TestCase
|
7
7
|
include TestHelper
|
8
8
|
include ActiveRecord::TestFixtures
|
9
9
|
|
10
10
|
self.fixture_path = File.join(File.dirname(__FILE__), 'fixtures')
|
11
|
-
fixtures :foos
|
11
|
+
fixtures :foos, :foo3ds
|
12
12
|
|
13
|
-
def ids_tester(method, args, ids = [])
|
14
|
-
geoms =
|
13
|
+
def ids_tester(method, args, ids = [], klass = Foo)
|
14
|
+
geoms = klass.send(method, *Array(args)).all
|
15
15
|
assert_equal(ids.sort, geoms.collect(&:id).sort)
|
16
16
|
end
|
17
17
|
|
@@ -120,11 +120,11 @@ if ENV['TEST_ACTIVERECORD']
|
|
120
120
|
assert_equal([2, 1, 3], Foo.order_by_distance_sphere('POINT(1 1)', :desc => true).all.collect(&:id))
|
121
121
|
end
|
122
122
|
|
123
|
-
def
|
123
|
+
def test_order_by_max_distance
|
124
124
|
assert_equal([1, 3, 2], Foo.order_by_maxdistance('POINT(1 1)').all.collect(&:id))
|
125
125
|
end
|
126
126
|
|
127
|
-
def
|
127
|
+
def test_order_by_max_distance_desc
|
128
128
|
assert_equal([2, 3, 1], Foo.order_by_maxdistance('POINT(1 1)', :desc => true).all.collect(&:id))
|
129
129
|
end
|
130
130
|
|
@@ -221,24 +221,35 @@ if ENV['TEST_ACTIVERECORD']
|
|
221
221
|
end
|
222
222
|
|
223
223
|
def test_order_by_length3d_spheroid_desc
|
224
|
-
|
225
|
-
|
224
|
+
expected = if Geos::ActiveRecord.POSTGIS[:lib] >= '2.0'
|
225
|
+
[3, 1, 2]
|
226
|
+
else
|
227
|
+
[1, 2, 3]
|
228
|
+
end
|
226
229
|
|
230
|
+
assert_equal(expected, Foo.order_by_length3d_spheroid('SPHEROID["WGS 84", 6378137, 298.257223563]', :desc => true).order('id').all.collect(&:id))
|
231
|
+
end
|
227
232
|
|
228
233
|
def test_order_by_length2d_spheroid
|
229
|
-
assert_equal([1, 2, 3], Foo.
|
234
|
+
assert_equal([1, 2, 3], Foo.order_by_length2d_spheroid('SPHEROID["WGS 84", 6378137, 298.257223563]').order('id').all.collect(&:id))
|
230
235
|
end
|
231
236
|
|
232
237
|
def test_order_by_length2d_spheroid_desc
|
233
|
-
assert_equal([1, 2
|
238
|
+
assert_equal([3, 1, 2], Foo.order_by_length2d_spheroid('SPHEROID["WGS 84", 6378137, 298.257223563]', :desc => true).order('id').all.collect(&:id))
|
234
239
|
end
|
235
240
|
|
236
241
|
def test_order_by_length_spheroid
|
237
|
-
assert_equal([1, 2, 3], Foo.
|
242
|
+
assert_equal([1, 2, 3], Foo.order_by_length_spheroid('SPHEROID["WGS 84", 6378137, 298.257223563]').order('id').all.collect(&:id))
|
238
243
|
end
|
239
244
|
|
240
245
|
def test_order_by_length_spheroid_desc
|
241
|
-
|
246
|
+
expected = if Geos::ActiveRecord.POSTGIS[:lib] >= '2.0'
|
247
|
+
[3, 1, 2]
|
248
|
+
else
|
249
|
+
[1, 2, 3]
|
250
|
+
end
|
251
|
+
|
252
|
+
assert_equal(expected, Foo.order_by_length_spheroid('SPHEROID["WGS 84", 6378137, 298.257223563]', :desc => true).order('id').all.collect(&:id))
|
242
253
|
end
|
243
254
|
|
244
255
|
def test_order_by_perimeter
|
@@ -277,7 +288,6 @@ if ENV['TEST_ACTIVERECORD']
|
|
277
288
|
assert_equal([1, 3, 2], Foo.order_by_hausdorffdistance('POINT(1 1)', 0.314).all.collect(&:id))
|
278
289
|
end
|
279
290
|
|
280
|
-
|
281
291
|
def test_order_by_distance_spheroid
|
282
292
|
assert_equal([2, 3, 1], Foo.order_by_distance_spheroid('POINT(10 10)', 'SPHEROID["WGS 84", 6378137, 298.257223563]').order('id').all.collect(&:id))
|
283
293
|
end
|
@@ -289,5 +299,35 @@ if ENV['TEST_ACTIVERECORD']
|
|
289
299
|
def test_order_by_area_with_desc_symbol
|
290
300
|
assert_equal([3, 1, 2], Foo.order_by_area(:desc).order('id').all.collect(&:id))
|
291
301
|
end
|
302
|
+
|
303
|
+
if Foo3d.respond_to?(:st_3dintersects)
|
304
|
+
def test_3dintersects
|
305
|
+
ids_tester(:st_3dintersects, 'LINESTRING(-5 -5 -5, 10 10 10)', [ 1, 3 ], Foo3d)
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
if Foo3d.respond_to?(:order_by_3ddistance)
|
310
|
+
def test_3ddistance
|
311
|
+
assert_equal([3, 2, 1], Foo3d.order_by_3ddistance('POINT(10 10)').order('id').all.collect(&:id))
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
if Foo3d.respond_to?(:order_by_3dmaxdistance)
|
316
|
+
def test_3dmaxdistance
|
317
|
+
assert_equal([2, 1, 3], Foo3d.order_by_3dmaxdistance('POINT(10 10)').order('id').all.collect(&:id))
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
if Foo3d.respond_to?(:st_3ddwithin)
|
322
|
+
def test_3ddwithin
|
323
|
+
ids_tester(:st_3ddwithin, [ 'LINESTRING(-5 -5 -5, 10 10 10)', 10 ], [ 1, 2, 3 ], Foo3d)
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
if Foo3d.respond_to?(:st_3ddfullywithin)
|
328
|
+
def test_3ddfullywithin
|
329
|
+
ids_tester(:st_3ddfullywithin, [ 'LINESTRING(-10 -10 -10, 10 10 10)', 100 ], [ 1, 2, 3 ], Foo3d)
|
330
|
+
end
|
331
|
+
end
|
292
332
|
end
|
293
333
|
end
|
data/test/test_helper.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
require 'rubygems'
|
3
3
|
require 'test/unit'
|
4
4
|
|
5
|
-
if ENV['TEST_ACTIVERECORD']
|
5
|
+
if ENV['TEST_ACTIVERECORD'] || defined?(IRB)
|
6
6
|
ACTIVERECORD_GEM_VERSION = ENV['ACTIVERECORD_GEM_VERSION'] || '~> 3.2.0'
|
7
7
|
gem 'activerecord', ACTIVERECORD_GEM_VERSION
|
8
8
|
|
@@ -36,7 +36,7 @@ if defined?(Geos::FFIGeos)
|
|
36
36
|
puts "Using #{Geos::FFIGeos.geos_library_paths.join(', ')}"
|
37
37
|
end
|
38
38
|
|
39
|
-
if ENV['TEST_ACTIVERECORD']
|
39
|
+
if ENV['TEST_ACTIVERECORD'] || defined?(IRB)
|
40
40
|
ActiveRecord::Base.logger = Logger.new("debug.log")
|
41
41
|
ActiveRecord::Base.configurations = {
|
42
42
|
'arunit' => {}
|
@@ -71,7 +71,7 @@ if ENV['TEST_ACTIVERECORD']
|
|
71
71
|
puts "Checking for PostGIS install"
|
72
72
|
2.times do
|
73
73
|
begin
|
74
|
-
if postgis_version =
|
74
|
+
if postgis_version = Geos::ActiveRecord.POSTGIS[:lib]
|
75
75
|
puts "PostGIS info from postgis_full_version(): #{postgis_version}"
|
76
76
|
break
|
77
77
|
end
|
@@ -104,14 +104,40 @@ if ENV['TEST_ACTIVERECORD']
|
|
104
104
|
t.text :name
|
105
105
|
end
|
106
106
|
|
107
|
-
ARBC.execute(%{SELECT AddGeometryColumn('public', 'foos', 'the_geom',
|
107
|
+
ARBC.execute(%{SELECT AddGeometryColumn('public', 'foos', 'the_geom', #{Geos::ActiveRecord.UNKNOWN_SRID}, 'GEOMETRY', 2)})
|
108
108
|
ARBC.execute(%{SELECT AddGeometryColumn('public', 'foos', 'the_other_geom', 4326, 'GEOMETRY', 2)})
|
109
109
|
end
|
110
110
|
|
111
|
+
if !ARBC.table_exists?('foo3ds')
|
112
|
+
ActiveRecord::Migration.create_table(:foo3ds) do |t|
|
113
|
+
t.text :name
|
114
|
+
end
|
115
|
+
|
116
|
+
ARBC.execute(%{SELECT AddGeometryColumn('public', 'foo3ds', 'the_geom', #{Geos::ActiveRecord.UNKNOWN_SRID}, 'GEOMETRY', 3)})
|
117
|
+
ARBC.execute(%{SELECT AddGeometryColumn('public', 'foo3ds', 'the_other_geom', 4326, 'GEOMETRY', 3)})
|
118
|
+
end
|
119
|
+
|
120
|
+
if !ARBC.table_exists?('foo_geographies')
|
121
|
+
ActiveRecord::Migration.create_table(:foo_geographies) do |t|
|
122
|
+
t.text :name
|
123
|
+
t.column :the_geom, :geography
|
124
|
+
t.column :the_other_geom, 'geography(Geometry, 4326)'
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
111
128
|
class Foo < ActiveRecord::Base
|
112
|
-
include Geos::ActiveRecord::
|
113
|
-
|
114
|
-
|
129
|
+
include Geos::ActiveRecord::SpatialColumns
|
130
|
+
create_spatial_column_accessors!
|
131
|
+
end
|
132
|
+
|
133
|
+
class Foo3d < ActiveRecord::Base
|
134
|
+
include Geos::ActiveRecord::SpatialColumns
|
135
|
+
create_spatial_column_accessors!
|
136
|
+
end
|
137
|
+
|
138
|
+
class FooGeography < ActiveRecord::Base
|
139
|
+
include Geos::ActiveRecord::SpatialColumns
|
140
|
+
create_spatial_column_accessors!
|
115
141
|
end
|
116
142
|
end
|
117
143
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: geos-extensions
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-05-23 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: Extensions for the GEOS library.
|
15
15
|
email: code@zoocasa.com
|
@@ -28,8 +28,8 @@ files:
|
|
28
28
|
- lib/geos-extensions.rb
|
29
29
|
- lib/geos/active_record_extensions.rb
|
30
30
|
- lib/geos/active_record_extensions/connection_adapters/postgresql_adapter.rb
|
31
|
-
- lib/geos/active_record_extensions/
|
32
|
-
- lib/geos/active_record_extensions/
|
31
|
+
- lib/geos/active_record_extensions/spatial_columns.rb
|
32
|
+
- lib/geos/active_record_extensions/spatial_scopes.rb
|
33
33
|
- lib/geos/geos_helper.rb
|
34
34
|
- lib/geos/google_maps.rb
|
35
35
|
- lib/geos/google_maps/api_2.rb
|
@@ -40,14 +40,18 @@ files:
|
|
40
40
|
- lib/tasks/test.rake
|
41
41
|
- test/adapter_tests.rb
|
42
42
|
- test/database.yml
|
43
|
+
- test/fixtures/foo3ds.yml
|
44
|
+
- test/fixtures/foo_geographies.yml
|
43
45
|
- test/fixtures/foos.yml
|
46
|
+
- test/geography_columns_tests.rb
|
44
47
|
- test/geometry_columns_tests.rb
|
45
|
-
- test/geospatial_scopes_tests.rb
|
46
48
|
- test/google_maps_api_2_tests.rb
|
47
49
|
- test/google_maps_api_3_tests.rb
|
48
50
|
- test/google_maps_polyline_encoder_tests.rb
|
49
51
|
- test/misc_tests.rb
|
50
52
|
- test/reader_tests.rb
|
53
|
+
- test/spatial_scopes_geographies_tests.rb
|
54
|
+
- test/spatial_scopes_tests.rb
|
51
55
|
- test/test_helper.rb
|
52
56
|
- test/writer_tests.rb
|
53
57
|
homepage: http://github.com/zoocasa/geos-extensions
|
@@ -70,7 +74,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
74
|
version: '0'
|
71
75
|
requirements: []
|
72
76
|
rubyforge_project:
|
73
|
-
rubygems_version: 1.8.
|
77
|
+
rubygems_version: 1.8.24
|
74
78
|
signing_key:
|
75
79
|
specification_version: 3
|
76
80
|
summary: Extensions for the GEOS library.
|
@@ -1,264 +0,0 @@
|
|
1
|
-
|
2
|
-
module Geos
|
3
|
-
module ActiveRecord #:nodoc:
|
4
|
-
|
5
|
-
# This little module helps us out with geometry columns. At least, in
|
6
|
-
# PostgreSQL it does.
|
7
|
-
#
|
8
|
-
# This module will add a method called geometry_columns to your model
|
9
|
-
# which will contain information that can be gleaned from the
|
10
|
-
# geometry_columns table that PostGIS creates.
|
11
|
-
#
|
12
|
-
# You can also have the module automagically create some accessor
|
13
|
-
# methods for you to make your life easier. These accessor methods will
|
14
|
-
# override the ActiveRecord defaults and allow you to set geometry
|
15
|
-
# column values using Geos geometry objects directly or with
|
16
|
-
# PostGIS-style extended WKT and such. See
|
17
|
-
# create_geometry_column_accessors! for details.
|
18
|
-
#
|
19
|
-
# === Caveats:
|
20
|
-
#
|
21
|
-
# * This module currently only works with PostGIS.
|
22
|
-
# * This module doesn't really "get" PostgreSQL catalogs and schemas
|
23
|
-
# and such. That would be a little more involved but it would be
|
24
|
-
# nice if Rails was aware of such things.
|
25
|
-
module GeometryColumns
|
26
|
-
GEOMETRY_COLUMN_OUTPUT_FORMATS = %w{ geos wkt wkb ewkt ewkb wkb_bin ewkb_bin }.freeze
|
27
|
-
|
28
|
-
class InvalidGeometry < ::ActiveRecord::ActiveRecordError
|
29
|
-
def initialize(geom)
|
30
|
-
super("Invalid geometry: #{geom}")
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
class SRIDNotFound < ::ActiveRecord::ActiveRecordError
|
35
|
-
def initialize(table_name, column)
|
36
|
-
super("Couldn't find SRID for #{table_name}.#{column}")
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
class CantConvertSRID < ::ActiveRecord::ActiveRecordError
|
41
|
-
def initialize(table_name, column, from_srid, to_srid)
|
42
|
-
super("Couldn't convert SRID for #{table_name}.#{column} from #{from_srid} to #{to_srid}")
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def self.included(base) #:nodoc:
|
47
|
-
base.extend(ClassMethods)
|
48
|
-
base.send(:include, Geos::ActiveRecord::GeospatialScopes)
|
49
|
-
end
|
50
|
-
|
51
|
-
module ClassMethods
|
52
|
-
protected
|
53
|
-
@geometry_columns = nil
|
54
|
-
|
55
|
-
public
|
56
|
-
# Returns an Array of available geometry columns in the
|
57
|
-
# table. These are PostgreSQLColumns with values set for
|
58
|
-
# the srid and coord_dimensions properties.
|
59
|
-
def geometry_columns
|
60
|
-
if @geometry_columns.nil?
|
61
|
-
@geometry_columns = connection.geometry_columns(self.table_name)
|
62
|
-
@geometry_columns.freeze
|
63
|
-
end
|
64
|
-
@geometry_columns
|
65
|
-
end
|
66
|
-
|
67
|
-
# Force a reload of available geometry columns.
|
68
|
-
def geometry_columns!
|
69
|
-
@geometry_columns = nil
|
70
|
-
geometry_columns
|
71
|
-
end
|
72
|
-
|
73
|
-
# Grabs a geometry column based on name.
|
74
|
-
def geometry_column_by_name(name)
|
75
|
-
@geometry_column_by_name ||= self.geometry_columns.inject(HashWithIndifferentAccess.new) do |memo, obj|
|
76
|
-
memo[obj.name] = obj
|
77
|
-
memo
|
78
|
-
end
|
79
|
-
@geometry_column_by_name[name]
|
80
|
-
end
|
81
|
-
|
82
|
-
# Quickly grab the SRID for a geometry column.
|
83
|
-
def srid_for(column)
|
84
|
-
self.geometry_column_by_name(column).try(:srid) || -1
|
85
|
-
end
|
86
|
-
|
87
|
-
# Quickly grab the number of dimensions for a geometry column.
|
88
|
-
def coord_dimension_for(column)
|
89
|
-
self.geometry_column_by_name(column).coord_dimension
|
90
|
-
end
|
91
|
-
|
92
|
-
protected
|
93
|
-
# Sets up nifty setters and getters for geometry columns.
|
94
|
-
# The methods created look like this:
|
95
|
-
#
|
96
|
-
# * geometry_column_name_geos
|
97
|
-
# * geometry_column_name_wkb
|
98
|
-
# * geometry_column_name_wkb_bin
|
99
|
-
# * geometry_column_name_wkt
|
100
|
-
# * geometry_column_name_ewkb
|
101
|
-
# * geometry_column_name_ewkb_bin
|
102
|
-
# * geometry_column_name_ewkt
|
103
|
-
# * geometry_column_name=(geom)
|
104
|
-
# * geometry_column_name(options = {})
|
105
|
-
#
|
106
|
-
# Where "geometry_column_name" is the name of the actual
|
107
|
-
# column.
|
108
|
-
#
|
109
|
-
# You can specify which geometry columns you want to apply
|
110
|
-
# these accessors using the :only and :except options.
|
111
|
-
def create_geometry_column_accessors!(options = nil)
|
112
|
-
create_these = if options.nil?
|
113
|
-
self.geometry_columns
|
114
|
-
elsif options[:except] && options[:only]
|
115
|
-
raise ArgumentError, "You can only specify either :except or :only (#{options.keys.inspect})"
|
116
|
-
elsif options[:except]
|
117
|
-
except = Array(options[:except]).collect(&:to_s)
|
118
|
-
self.geometry_columns.reject { |c| except.include?(c) }
|
119
|
-
elsif options[:only]
|
120
|
-
only = Array(options[:only]).collect(&:to_s)
|
121
|
-
self.geometry_columns.select { |c| only.include?(c) }
|
122
|
-
end
|
123
|
-
|
124
|
-
create_these.each do |k|
|
125
|
-
src, line = <<-EOF, __LINE__ + 1
|
126
|
-
def #{k.name}=(geom)
|
127
|
-
column_srid = self.class.srid_for(#{k.name.inspect})
|
128
|
-
|
129
|
-
if geom =~ /^SRID=default;/i
|
130
|
-
geom = geom.sub(/default/i, column_srid.to_s)
|
131
|
-
end
|
132
|
-
|
133
|
-
geos = Geos.read(geom)
|
134
|
-
|
135
|
-
geom_srid = if geos.srid == 0
|
136
|
-
-1
|
137
|
-
else
|
138
|
-
geos.srid
|
139
|
-
end
|
140
|
-
|
141
|
-
if column_srid != geom_srid
|
142
|
-
if column_srid == -1 || geom_srid == -1
|
143
|
-
geos.srid = column_srid
|
144
|
-
else
|
145
|
-
raise CantConvertSRID.new(self.class.table_name, #{k.name.inspect}, geom_srid, column_srid)
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
self['#{k.name}'] = geos.to_ewkb
|
150
|
-
|
151
|
-
GEOMETRY_COLUMN_OUTPUT_FORMATS.each do |f|
|
152
|
-
instance_variable_set("@#{k.name}_\#{f}", nil)
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
def #{k.name}_geos
|
157
|
-
@#{k.name}_geos ||= Geos.from_wkb(self['#{k.name}'])
|
158
|
-
end
|
159
|
-
|
160
|
-
def #{k.name}(options = {})
|
161
|
-
format = case options
|
162
|
-
when String, Symbol
|
163
|
-
options
|
164
|
-
when Hash
|
165
|
-
options = options.stringify_keys
|
166
|
-
options['format'] if options['format']
|
167
|
-
end
|
168
|
-
|
169
|
-
if format
|
170
|
-
if GEOMETRY_COLUMN_OUTPUT_FORMATS.include?(format)
|
171
|
-
return self.send(:"#{k.name}_\#{format}")
|
172
|
-
else
|
173
|
-
raise ArgumentError, "Invalid option: \#{options[:format]}"
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
self['#{k.name}']
|
178
|
-
end
|
179
|
-
EOF
|
180
|
-
self.class_eval(src, __FILE__, line)
|
181
|
-
|
182
|
-
GEOMETRY_COLUMN_OUTPUT_FORMATS.reject { |f| f == 'geos' }.each do |f|
|
183
|
-
src, line = <<-EOF, __LINE__ + 1
|
184
|
-
def #{k.name}_#{f}(*args)
|
185
|
-
@#{k.name}_#{f} ||= self.#{k.name}_geos.to_#{f}(*args) rescue nil
|
186
|
-
end
|
187
|
-
EOF
|
188
|
-
self.class_eval(src, __FILE__, line)
|
189
|
-
end
|
190
|
-
end
|
191
|
-
end
|
192
|
-
|
193
|
-
# Stubs for documentation purposes:
|
194
|
-
|
195
|
-
# Returns a Geos geometry.
|
196
|
-
def __geometry_column_name_geos; end
|
197
|
-
|
198
|
-
# Returns a hex-encoded WKB String.
|
199
|
-
def __geometry_column_name_wkb; end
|
200
|
-
|
201
|
-
# Returns a WKB String in binary.
|
202
|
-
def __geometry_column_name_wkb_bin; end
|
203
|
-
|
204
|
-
# Returns a WKT String.
|
205
|
-
def __geometry_column_name_wkt; end
|
206
|
-
|
207
|
-
# Returns a hex-encoded EWKB String.
|
208
|
-
def __geometry_column_name_ewkb; end
|
209
|
-
|
210
|
-
# Returns an EWKB String in binary.
|
211
|
-
def __geometry_column_name_ewkb_bin; end
|
212
|
-
|
213
|
-
# Returns an EWKT String.
|
214
|
-
def __geometry_column_name_ewkt; end
|
215
|
-
|
216
|
-
# An enhanced setter that tries to deduce how you're
|
217
|
-
# setting the value. The setter can handle Geos::Geometry
|
218
|
-
# objects, WKT, EWKT and WKB and EWKB in both hex and
|
219
|
-
# binary.
|
220
|
-
#
|
221
|
-
# When dealing with SRIDs, you can have the SRID set
|
222
|
-
# automatically on WKT by setting the value as
|
223
|
-
# "SRID=default;GEOMETRY(...)", i.e.:
|
224
|
-
#
|
225
|
-
# geometry_column_name = "SRID=default;POINT(1.0 1.0)"
|
226
|
-
#
|
227
|
-
# The SRID will be filled in automatically if available.
|
228
|
-
# Note that we're only setting the SRID on the geometry,
|
229
|
-
# but we're not doing any sort of re-projection or anything
|
230
|
-
# of the sort. If you need to convert from one SRID to
|
231
|
-
# another, you're stuck for the moment, but we'll be adding
|
232
|
-
# support for reprojections/transoformations via proj4rb
|
233
|
-
# soon.
|
234
|
-
#
|
235
|
-
# For WKB, you're better off manipulating the WKB directly
|
236
|
-
# or using proper Geos geometry objects.
|
237
|
-
def __geometry_column_name=(geom); end
|
238
|
-
|
239
|
-
# An enhanced getter that accepts an options Hash or
|
240
|
-
# String/Symbol that can be used to determine the output
|
241
|
-
# format. In the options Hash, use :format, or set the
|
242
|
-
# format directly as a String or Symbol.
|
243
|
-
#
|
244
|
-
# This basically allows you to do the following, which
|
245
|
-
# are equivalent:
|
246
|
-
#
|
247
|
-
# geometry_column_name(:wkt)
|
248
|
-
# geometry_column_name(:format => :wkt)
|
249
|
-
# geometry_column_name_wkt
|
250
|
-
def __geometry_column_name(options = {}); end
|
251
|
-
|
252
|
-
undef __geometry_column_name_geos
|
253
|
-
undef __geometry_column_name_wkb
|
254
|
-
undef __geometry_column_name_wkb_bin
|
255
|
-
undef __geometry_column_name_wkt
|
256
|
-
undef __geometry_column_name_ewkb
|
257
|
-
undef __geometry_column_name_ewkb_bin
|
258
|
-
undef __geometry_column_name_ewkt
|
259
|
-
undef __geometry_column_name=
|
260
|
-
undef __geometry_column_name
|
261
|
-
end
|
262
|
-
end
|
263
|
-
end
|
264
|
-
end
|