ar-postgis 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Documentation.rdoc +292 -0
- data/History.rdoc +147 -0
- data/LICENSE.txt +29 -0
- data/README.rdoc +78 -0
- data/lib/active_record/connection_adapters/postgis_adapter.rb +38 -0
- data/lib/active_record/connection_adapters/postgis_adapter/rails4/create_connection.rb +29 -0
- data/lib/active_record/connection_adapters/postgis_adapter/rails4/databases.rake +17 -0
- data/lib/active_record/connection_adapters/postgis_adapter/rails4/main_adapter.rb +202 -0
- data/lib/active_record/connection_adapters/postgis_adapter/rails4/postgis_database_tasks.rb +204 -0
- data/lib/active_record/connection_adapters/postgis_adapter/rails4/spatial_column.rb +184 -0
- data/lib/active_record/connection_adapters/postgis_adapter/rails4/spatial_table_definition.rb +121 -0
- data/lib/active_record/connection_adapters/postgis_adapter/railtie.rb +3 -0
- data/lib/active_record/connection_adapters/postgis_adapter/shared/arel_tosql.rb +23 -0
- data/lib/active_record/connection_adapters/postgis_adapter/shared/common_adapter_methods.rb +46 -0
- data/lib/active_record/connection_adapters/postgis_adapter/shared/railtie.rb +13 -0
- data/lib/active_record/connection_adapters/postgis_adapter/shared/setup.rb +21 -0
- data/lib/active_record/connection_adapters/postgis_adapter/version.rb +7 -0
- data/lib/activerecord-postgis-adapter.rb +1 -0
- data/test/database.yml +5 -0
- data/test/tc_basic.rb +212 -0
- data/test/tc_ddl.rb +303 -0
- data/test/tc_nested_class.rb +48 -0
- data/test/tc_spatial_queries.rb +126 -0
- data/test/tc_tasks.rb +112 -0
- metadata +149 -0
@@ -0,0 +1,184 @@
|
|
1
|
+
module ActiveRecord # :nodoc:
|
2
|
+
|
3
|
+
module ConnectionAdapters # :nodoc:
|
4
|
+
|
5
|
+
module PostGISAdapter # :nodoc:
|
6
|
+
|
7
|
+
|
8
|
+
class SpatialColumn < ConnectionAdapters::PostgreSQLColumn # :nodoc:
|
9
|
+
|
10
|
+
|
11
|
+
def initialize(factory_settings_, table_name_, name_, default_, oid_type_, sql_type_=nil, null_=true, opts_=nil)
|
12
|
+
@factory_settings = factory_settings_
|
13
|
+
@table_name = table_name_
|
14
|
+
@geographic = sql_type_ =~ /geography/i ? true : false
|
15
|
+
if opts_
|
16
|
+
# This case comes from an entry in the geometry_columns table
|
17
|
+
@geometric_type = ::RGeo::ActiveRecord.geometric_type_from_name(opts_[:type]) ||
|
18
|
+
::RGeo::Feature::Geometry
|
19
|
+
@srid = opts_[:srid].to_i
|
20
|
+
@has_z = opts_[:has_z] ? true : false
|
21
|
+
@has_m = opts_[:has_m] ? true : false
|
22
|
+
elsif @geographic
|
23
|
+
# Geographic type information is embedded in the SQL type
|
24
|
+
@geometric_type = ::RGeo::Feature::Geometry
|
25
|
+
@srid = 4326
|
26
|
+
@has_z = @has_m = false
|
27
|
+
if sql_type_ =~ /geography\((.*)\)$/i
|
28
|
+
params_ = $1.split(',')
|
29
|
+
if params_.size >= 2
|
30
|
+
if params_.first =~ /([a-z]+[^zm])(z?)(m?)/i
|
31
|
+
@has_z = $2.length > 0
|
32
|
+
@has_m = $3.length > 0
|
33
|
+
@geometric_type = ::RGeo::ActiveRecord.geometric_type_from_name($1)
|
34
|
+
end
|
35
|
+
if params_.last =~ /(\d+)/
|
36
|
+
@srid = $1.to_i
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
elsif sql_type_ =~ /geography|geometry|point|linestring|polygon/i
|
41
|
+
# Just in case there is a geometry column with no geometry_columns entry.
|
42
|
+
@geometric_type = ::RGeo::Feature::Geometry
|
43
|
+
@srid = @has_z = @has_m = nil
|
44
|
+
else
|
45
|
+
# Non-spatial column
|
46
|
+
@geometric_type = @has_z = @has_m = @srid = nil
|
47
|
+
end
|
48
|
+
super(name_, default_, oid_type_, sql_type_, null_)
|
49
|
+
if spatial?
|
50
|
+
if @srid
|
51
|
+
@limit = {:srid => @srid, :type => @geometric_type.type_name.underscore}
|
52
|
+
@limit[:has_z] = true if @has_z
|
53
|
+
@limit[:has_m] = true if @has_m
|
54
|
+
@limit[:geographic] = true if @geographic
|
55
|
+
else
|
56
|
+
@limit = {:no_constraints => true}
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
attr_reader :geographic
|
63
|
+
attr_reader :srid
|
64
|
+
attr_reader :geometric_type
|
65
|
+
attr_reader :has_z
|
66
|
+
attr_reader :has_m
|
67
|
+
|
68
|
+
alias_method :geographic?, :geographic
|
69
|
+
alias_method :has_z?, :has_z
|
70
|
+
alias_method :has_m?, :has_m
|
71
|
+
|
72
|
+
|
73
|
+
def spatial?
|
74
|
+
type == :spatial || type == :geography
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
def has_spatial_constraints?
|
79
|
+
!@srid.nil?
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
def klass
|
84
|
+
spatial? ? ::RGeo::Feature::Geometry : super
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
def type_cast(value_)
|
89
|
+
if spatial?
|
90
|
+
SpatialColumn.convert_to_geometry(value_, @factory_settings, @table_name, name,
|
91
|
+
@geographic, @srid, @has_z, @has_m)
|
92
|
+
else
|
93
|
+
super
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
|
101
|
+
def simplified_type(sql_type_)
|
102
|
+
sql_type_ =~ /geography|geometry|point|linestring|polygon/i ? :spatial : super
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
def self.convert_to_geometry(input_, factory_settings_, table_name_, column_, geographic_, srid_, has_z_, has_m_)
|
107
|
+
if srid_
|
108
|
+
constraints_ = {:geographic => geographic_, :has_z_coordinate => has_z_,
|
109
|
+
:has_m_coordinate => has_m_, :srid => srid_}
|
110
|
+
else
|
111
|
+
constraints_ = nil
|
112
|
+
end
|
113
|
+
if ::RGeo::Feature::Geometry === input_
|
114
|
+
factory_ = factory_settings_.get_column_factory(table_name_, column_, constraints_)
|
115
|
+
::RGeo::Feature.cast(input_, factory_) rescue nil
|
116
|
+
elsif input_.respond_to?(:to_str)
|
117
|
+
input_ = input_.to_str
|
118
|
+
if input_.length == 0
|
119
|
+
nil
|
120
|
+
else
|
121
|
+
factory_ = factory_settings_.get_column_factory(table_name_, column_, constraints_)
|
122
|
+
marker_ = input_[0,1]
|
123
|
+
if marker_ == "\x00" || marker_ == "\x01" || input_[0,4] =~ /[0-9a-fA-F]{4}/
|
124
|
+
::RGeo::WKRep::WKBParser.new(factory_, :support_ewkb => true).parse(input_) rescue nil
|
125
|
+
else
|
126
|
+
::RGeo::WKRep::WKTParser.new(factory_, :support_ewkt => true).parse(input_) rescue nil
|
127
|
+
end
|
128
|
+
end
|
129
|
+
else
|
130
|
+
nil
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
# Register spatial types with the postgres OID mechanism
|
139
|
+
# so we can recognize custom columns coming from the database.
|
140
|
+
|
141
|
+
class SpatialOID < PostgreSQLAdapter::OID::Type # :nodoc:
|
142
|
+
|
143
|
+
def initialize(factory_generator_)
|
144
|
+
@factory_generator = factory_generator_
|
145
|
+
end
|
146
|
+
|
147
|
+
def type_cast(value_)
|
148
|
+
return if value_.nil?
|
149
|
+
::RGeo::WKRep::WKBParser.new(@factory_generator, :support_ewkb => true).parse(value_) rescue nil
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
PostgreSQLAdapter::OID.register_type('geometry', SpatialOID.new(nil))
|
155
|
+
PostgreSQLAdapter::OID.register_type('geography', SpatialOID.new(::RGeo::Geographic.method(:spherical_factory)))
|
156
|
+
|
157
|
+
|
158
|
+
# This is a hack to ActiveRecord::ModelSchema. We have to "decorate" the decorate_columns
|
159
|
+
# method to apply class-specific customizations to spatial type casting.
|
160
|
+
|
161
|
+
module DecorateColumnsModification # :nodoc:
|
162
|
+
|
163
|
+
def decorate_columns(columns_hash_)
|
164
|
+
columns_hash_ = super(columns_hash_)
|
165
|
+
return unless columns_hash_
|
166
|
+
canonical_columns_ = self.columns_hash
|
167
|
+
columns_hash_.each do |name_, col_|
|
168
|
+
if col_.is_a?(SpatialOID) && (canonical_ = canonical_columns_[name_]) && canonical_.spatial?
|
169
|
+
columns_hash_[name_] = canonical_
|
170
|
+
end
|
171
|
+
end
|
172
|
+
columns_hash_
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
|
177
|
+
::ActiveRecord::Base.extend(DecorateColumnsModification)
|
178
|
+
|
179
|
+
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module ActiveRecord # :nodoc:
|
2
|
+
|
3
|
+
module ConnectionAdapters # :nodoc:
|
4
|
+
|
5
|
+
module PostGISAdapter # :nodoc:
|
6
|
+
|
7
|
+
|
8
|
+
class TableDefinition < ConnectionAdapters::PostgreSQLAdapter::TableDefinition # :nodoc:
|
9
|
+
|
10
|
+
def initialize(types_, name_, temporary_, options_, as_, base_)
|
11
|
+
@base = base_
|
12
|
+
@spatial_columns_hash = {}
|
13
|
+
super(types_, name_, temporary_, options_, as_)
|
14
|
+
end
|
15
|
+
|
16
|
+
def column(name_, type_, options_={})
|
17
|
+
if (info_ = @base.spatial_column_constructor(type_.to_sym))
|
18
|
+
type_ = options_[:type] || info_[:type] || type_
|
19
|
+
if type_.to_s == 'geometry' &&
|
20
|
+
(options_[:no_constraints] ||
|
21
|
+
options_[:limit].is_a?(::Hash) && options_[:limit][:no_constraints])
|
22
|
+
then
|
23
|
+
options_.delete(:limit)
|
24
|
+
else
|
25
|
+
options_[:type] = type_
|
26
|
+
type_ = :spatial
|
27
|
+
end
|
28
|
+
end
|
29
|
+
if type_ == :spatial
|
30
|
+
if (limit_ = options_.delete(:limit))
|
31
|
+
options_.merge!(limit_) if limit_.is_a?(::Hash)
|
32
|
+
end
|
33
|
+
if options_[:geographic]
|
34
|
+
type_ = :geography
|
35
|
+
spatial_type_ = (options_[:type] || 'geometry').to_s.upcase.gsub('_', '')
|
36
|
+
spatial_type_ << 'Z' if options_[:has_z]
|
37
|
+
spatial_type_ << 'M' if options_[:has_m]
|
38
|
+
options_[:limit] = "#{spatial_type_},#{options_[:srid] || 4326}"
|
39
|
+
end
|
40
|
+
name_ = name_.to_s
|
41
|
+
if primary_key_column_name == name_
|
42
|
+
raise ArgumentError, "you can't redefine the primary key column '#{name_}'. To define a custom primary key, pass { id: false } to create_table."
|
43
|
+
end
|
44
|
+
col_ = new_column_definition(name_, type_, options_)
|
45
|
+
col_.set_spatial_type(options_[:type])
|
46
|
+
col_.set_geographic(options_[:geographic])
|
47
|
+
col_.set_srid(options_[:srid])
|
48
|
+
col_.set_has_z(options_[:has_z])
|
49
|
+
col_.set_has_m(options_[:has_m])
|
50
|
+
(col_.geographic? ? @columns_hash : @spatial_columns_hash)[name_] = col_
|
51
|
+
else
|
52
|
+
super(name_, type_, options_)
|
53
|
+
end
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
def create_column_definition(name_, type_)
|
58
|
+
if type_ == :spatial || type_ == :geography
|
59
|
+
PostGISAdapter::ColumnDefinition.new(name_, type_)
|
60
|
+
else
|
61
|
+
super
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def non_geographic_spatial_columns
|
66
|
+
@spatial_columns_hash.values
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
class ColumnDefinition < ConnectionAdapters::ColumnDefinition # :nodoc:
|
73
|
+
|
74
|
+
def spatial_type
|
75
|
+
@spatial_type
|
76
|
+
end
|
77
|
+
|
78
|
+
def geographic?
|
79
|
+
@geographic
|
80
|
+
end
|
81
|
+
|
82
|
+
def srid
|
83
|
+
@srid ? @srid.to_i : (geographic? ? 4326 : -1)
|
84
|
+
end
|
85
|
+
|
86
|
+
def has_z?
|
87
|
+
@has_z
|
88
|
+
end
|
89
|
+
|
90
|
+
def has_m?
|
91
|
+
@has_m
|
92
|
+
end
|
93
|
+
|
94
|
+
def set_geographic(value_)
|
95
|
+
@geographic = value_ ? true : false
|
96
|
+
end
|
97
|
+
|
98
|
+
def set_spatial_type(value_)
|
99
|
+
@spatial_type = value_.to_s
|
100
|
+
end
|
101
|
+
|
102
|
+
def set_srid(value_)
|
103
|
+
@srid = value_
|
104
|
+
end
|
105
|
+
|
106
|
+
def set_has_z(value_)
|
107
|
+
@has_z = value_ ? true : false
|
108
|
+
end
|
109
|
+
|
110
|
+
def set_has_m(value_)
|
111
|
+
@has_m = value_ ? true : false
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Arel # :nodoc:
|
2
|
+
module Visitors # :nodoc:
|
3
|
+
|
4
|
+
class PostGIS < PostgreSQL # :nodoc:
|
5
|
+
|
6
|
+
FUNC_MAP = {
|
7
|
+
'st_wkttosql' => 'ST_GeomFromEWKT',
|
8
|
+
}
|
9
|
+
|
10
|
+
include ::RGeo::ActiveRecord::SpatialToSql
|
11
|
+
|
12
|
+
def st_func(standard_name_)
|
13
|
+
FUNC_MAP[standard_name_.downcase] || standard_name_
|
14
|
+
end
|
15
|
+
|
16
|
+
alias_method :visit_in_spatial_context, :visit
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
VISITORS['postgis'] = ::Arel::Visitors::PostGIS
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module ActiveRecord # :nodoc:
|
2
|
+
module ConnectionAdapters # :nodoc:
|
3
|
+
module PostGISAdapter # :nodoc:
|
4
|
+
SPATIAL_COLUMN_CONSTRUCTORS = ::RGeo::ActiveRecord::DEFAULT_SPATIAL_COLUMN_CONSTRUCTORS.merge(
|
5
|
+
:geography => {:type => 'geometry', :geographic => true}
|
6
|
+
)
|
7
|
+
|
8
|
+
module CommonAdapterMethods # :nodoc:
|
9
|
+
def set_rgeo_factory_settings(factory_settings_)
|
10
|
+
@rgeo_factory_settings = factory_settings_
|
11
|
+
end
|
12
|
+
|
13
|
+
def adapter_name
|
14
|
+
PostGISAdapter::ADAPTER_NAME
|
15
|
+
end
|
16
|
+
|
17
|
+
def spatial_column_constructor(name_)
|
18
|
+
PostGISAdapter::SPATIAL_COLUMN_CONSTRUCTORS[name_]
|
19
|
+
end
|
20
|
+
|
21
|
+
def postgis_lib_version
|
22
|
+
@postgis_lib_version ||= select_value("SELECT PostGIS_Lib_Version()")
|
23
|
+
end
|
24
|
+
|
25
|
+
# http://postgis.17.x6.nabble.com/Default-SRID-td5001115.html
|
26
|
+
def default_srid
|
27
|
+
0
|
28
|
+
end
|
29
|
+
|
30
|
+
def srs_database_columns
|
31
|
+
{:srtext_column => 'srtext', :proj4text_column => 'proj4text', :auth_name_column => 'auth_name', :auth_srid_column => 'auth_srid'}
|
32
|
+
end
|
33
|
+
|
34
|
+
def quote(value_, column_=nil)
|
35
|
+
if ::RGeo::Feature::Geometry.check_type(value_)
|
36
|
+
"'#{::RGeo::WKRep::WKBGenerator.new(:hex_format => true, :type_format => :ewkb, :emit_ewkb_srid => true).generate(value_)}'"
|
37
|
+
elsif value_.is_a?(::RGeo::Cartesian::BoundingBox)
|
38
|
+
"'#{value_.min_x},#{value_.min_y},#{value_.max_x},#{value_.max_y}'::box"
|
39
|
+
else
|
40
|
+
super
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
unless defined?(::ActiveRecord::ConnectionAdapters::PostGISAdapter::Railtie)
|
2
|
+
module ActiveRecord # :nodoc:
|
3
|
+
module ConnectionAdapters # :nodoc:
|
4
|
+
module PostGISAdapter # :nodoc:
|
5
|
+
class Railtie < ::Rails::Railtie # :nodoc:
|
6
|
+
rake_tasks do
|
7
|
+
load ::File.expand_path("../rails4/databases.rake", ::File.dirname(__FILE__))
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ActiveRecord # :nodoc:
|
2
|
+
|
3
|
+
module ConnectionAdapters # :nodoc:
|
4
|
+
|
5
|
+
module PostGISAdapter # :nodoc:
|
6
|
+
|
7
|
+
|
8
|
+
def self.initial_setup
|
9
|
+
gis_ignore_tables_ = ['geometry_columns', 'spatial_ref_sys', 'layer', 'topology']
|
10
|
+
ignore_tables_ = ::ActiveRecord::SchemaDumper.ignore_tables
|
11
|
+
gis_ignore_tables_.each do |table_|
|
12
|
+
ignore_tables_ << table_ unless ignore_tables_.include?(table_)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'active_record/connection_adapters/postgis_adapter.rb'
|
data/test/database.yml
ADDED
data/test/tc_basic.rb
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'rgeo/active_record/adapter_test_helper'
|
3
|
+
|
4
|
+
|
5
|
+
module RGeo
|
6
|
+
module ActiveRecord # :nodoc:
|
7
|
+
module PostGISAdapter # :nodoc:
|
8
|
+
module Tests # :nodoc:
|
9
|
+
|
10
|
+
class TestBasic < ::MiniTest::Test # :nodoc:
|
11
|
+
|
12
|
+
DATABASE_CONFIG_PATH = ::File.dirname(__FILE__)+'/database.yml'
|
13
|
+
OVERRIDE_DATABASE_CONFIG_PATH = ::File.dirname(__FILE__)+'/database_local.yml'
|
14
|
+
|
15
|
+
include AdapterTestHelper
|
16
|
+
|
17
|
+
define_test_methods do
|
18
|
+
|
19
|
+
|
20
|
+
def populate_ar_class(content_)
|
21
|
+
klass_ = create_ar_class
|
22
|
+
case content_
|
23
|
+
when :mercator_point
|
24
|
+
klass_.connection.create_table(:spatial_test) do |t_|
|
25
|
+
t_.column 'latlon', :point, :srid => 3785
|
26
|
+
end
|
27
|
+
when :latlon_point_geographic
|
28
|
+
klass_.connection.create_table(:spatial_test) do |t_|
|
29
|
+
t_.column 'latlon', :point, :srid => 4326, :geographic => true
|
30
|
+
end
|
31
|
+
when :no_constraints
|
32
|
+
klass_.connection.create_table(:spatial_test) do |t_|
|
33
|
+
t_.column 'geo', :geometry, :no_constraints => true
|
34
|
+
end
|
35
|
+
end
|
36
|
+
klass_
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
def test_version
|
41
|
+
refute_nil(::ActiveRecord::ConnectionAdapters::PostGISAdapter::VERSION)
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
def test_postgis_available
|
46
|
+
connection_ = create_ar_class.connection
|
47
|
+
assert_equal('PostGIS', connection_.adapter_name)
|
48
|
+
refute_nil(connection_.postgis_lib_version)
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
def test_set_and_get_point
|
53
|
+
klass_ = populate_ar_class(:mercator_point)
|
54
|
+
obj_ = klass_.new
|
55
|
+
assert_nil(obj_.latlon)
|
56
|
+
obj_.latlon = @factory.point(1.0, 2.0)
|
57
|
+
assert_equal(@factory.point(1.0, 2.0), obj_.latlon)
|
58
|
+
assert_equal(3785, obj_.latlon.srid)
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
def test_set_and_get_point_from_wkt
|
63
|
+
klass_ = populate_ar_class(:mercator_point)
|
64
|
+
obj_ = klass_.new
|
65
|
+
assert_nil(obj_.latlon)
|
66
|
+
obj_.latlon = 'POINT(1 2)'
|
67
|
+
assert_equal(@factory.point(1.0, 2.0), obj_.latlon)
|
68
|
+
assert_equal(3785, obj_.latlon.srid)
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
def test_save_and_load_point
|
73
|
+
klass_ = populate_ar_class(:mercator_point)
|
74
|
+
obj_ = klass_.new
|
75
|
+
obj_.latlon = @factory.point(1.0, 2.0)
|
76
|
+
obj_.save!
|
77
|
+
id_ = obj_.id
|
78
|
+
obj2_ = klass_.find(id_)
|
79
|
+
assert_equal(@factory.point(1.0, 2.0), obj2_.latlon)
|
80
|
+
assert_equal(3785, obj2_.latlon.srid)
|
81
|
+
assert_equal(true, ::RGeo::Geos.is_geos?(obj2_.latlon))
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
def test_save_and_load_geographic_point
|
86
|
+
klass_ = populate_ar_class(:latlon_point_geographic)
|
87
|
+
obj_ = klass_.new
|
88
|
+
obj_.latlon = @factory.point(1.0, 2.0)
|
89
|
+
obj_.save!
|
90
|
+
id_ = obj_.id
|
91
|
+
obj2_ = klass_.find(id_)
|
92
|
+
assert_equal(@geographic_factory.point(1.0, 2.0), obj2_.latlon)
|
93
|
+
assert_equal(4326, obj2_.latlon.srid)
|
94
|
+
assert_equal(false, ::RGeo::Geos.is_geos?(obj2_.latlon))
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
def test_save_and_load_point_from_wkt
|
99
|
+
klass_ = populate_ar_class(:mercator_point)
|
100
|
+
obj_ = klass_.new
|
101
|
+
obj_.latlon = 'POINT(1 2)'
|
102
|
+
obj_.save!
|
103
|
+
id_ = obj_.id
|
104
|
+
obj2_ = klass_.find(id_)
|
105
|
+
assert_equal(@factory.point(1.0, 2.0), obj2_.latlon)
|
106
|
+
assert_equal(3785, obj2_.latlon.srid)
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
def test_set_point_bad_wkt
|
111
|
+
klass_ = populate_ar_class(:mercator_point)
|
112
|
+
obj_ = klass_.create(:latlon => 'POINT (x)')
|
113
|
+
assert_nil(obj_.latlon)
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
def test_set_point_wkt_wrong_type
|
118
|
+
klass_ = populate_ar_class(:mercator_point)
|
119
|
+
assert_raises(::ActiveRecord::StatementInvalid) do
|
120
|
+
klass_.create(:latlon => 'LINESTRING(1 2, 3 4, 5 6)')
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
def test_custom_factory
|
126
|
+
klass_ = create_ar_class
|
127
|
+
klass_.connection.create_table(:spatial_test) do |t_|
|
128
|
+
t_.point(:latlon, :srid => 4326)
|
129
|
+
end
|
130
|
+
factory_ = ::RGeo::Geographic.simple_mercator_factory
|
131
|
+
klass_.class_eval do
|
132
|
+
set_rgeo_factory_for_column(:latlon, factory_)
|
133
|
+
end
|
134
|
+
rec_ = klass_.new
|
135
|
+
rec_.latlon = 'POINT(-122 47)'
|
136
|
+
assert_equal(factory_, rec_.latlon.factory)
|
137
|
+
rec_.save!
|
138
|
+
assert_equal(factory_, rec_.latlon.factory)
|
139
|
+
rec2_ = klass_.find(rec_.id)
|
140
|
+
assert_equal(factory_, rec2_.latlon.factory)
|
141
|
+
end
|
142
|
+
|
143
|
+
|
144
|
+
def test_readme_example
|
145
|
+
klass_ = create_ar_class
|
146
|
+
klass_.connection.create_table(:spatial_test) do |t_|
|
147
|
+
t_.column(:shape, :geometry)
|
148
|
+
t_.line_string(:path, :srid => 3785)
|
149
|
+
t_.point(:latlon, :geographic => true)
|
150
|
+
end
|
151
|
+
klass_.connection.change_table(:spatial_test) do |t_|
|
152
|
+
t_.index(:latlon, :spatial => true)
|
153
|
+
end
|
154
|
+
klass_.class_eval do
|
155
|
+
self.rgeo_factory_generator = ::RGeo::Geos.method(:factory)
|
156
|
+
set_rgeo_factory_for_column(:latlon, ::RGeo::Geographic.spherical_factory)
|
157
|
+
end
|
158
|
+
rec_ = klass_.new
|
159
|
+
rec_.latlon = 'POINT(-122 47)'
|
160
|
+
loc_ = rec_.latlon
|
161
|
+
assert_equal(47, loc_.latitude)
|
162
|
+
rec_.shape = loc_
|
163
|
+
assert_equal(true, ::RGeo::Geos.is_geos?(rec_.shape))
|
164
|
+
end
|
165
|
+
|
166
|
+
|
167
|
+
# no_constraints no longer supported in PostGIS 2.0
|
168
|
+
def _test_save_and_load_no_constraints
|
169
|
+
klass_ = populate_ar_class(:no_constraints)
|
170
|
+
factory1_ = ::RGeo::Cartesian.preferred_factory(:srid => 3785)
|
171
|
+
factory2_ = ::RGeo::Cartesian.preferred_factory(:srid => 2000)
|
172
|
+
obj_ = klass_.new
|
173
|
+
obj_.geo = factory1_.point(1.0, 2.0)
|
174
|
+
obj_.save!
|
175
|
+
id_ = obj_.id
|
176
|
+
obj2_ = klass_.find(id_)
|
177
|
+
assert_equal(factory1_.point(1.0, 2.0), obj2_.geo)
|
178
|
+
assert_equal(3785, obj2_.geo.srid)
|
179
|
+
obj2_.geo = factory2_.point(3.0, 4.0)
|
180
|
+
obj2_.save!
|
181
|
+
obj3_ = klass_.find(id_)
|
182
|
+
assert_equal(factory2_.point(3.0, 4.0), obj3_.geo)
|
183
|
+
assert_equal(2000, obj3_.geo.srid)
|
184
|
+
end
|
185
|
+
|
186
|
+
|
187
|
+
def test_point_to_json
|
188
|
+
klass_ = populate_ar_class(:mercator_point)
|
189
|
+
obj_ = klass_.new
|
190
|
+
assert_match(/"latlon":null/, obj_.to_json)
|
191
|
+
obj_.latlon = @factory.point(1.0, 2.0)
|
192
|
+
assert_match(/"latlon":"POINT\s\(1\.0\s2\.0\)"/, obj_.to_json)
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
def test_custom_column
|
197
|
+
klass_ = populate_ar_class(:mercator_point)
|
198
|
+
rec_ = klass_.new
|
199
|
+
rec_.latlon = 'POINT(0 0)'
|
200
|
+
rec_.save
|
201
|
+
refute_nil(klass_.select("CURRENT_TIMESTAMP as ts").first.ts)
|
202
|
+
end
|
203
|
+
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|