activerecord-postgis-adapter 0.2.3 → 0.3.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.
@@ -0,0 +1,63 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # PostGIS adapter for ActiveRecord
4
+ #
5
+ # -----------------------------------------------------------------------------
6
+ # Copyright 2010 Daniel Azuma
7
+ #
8
+ # All rights reserved.
9
+ #
10
+ # Redistribution and use in source and binary forms, with or without
11
+ # modification, are permitted provided that the following conditions are met:
12
+ #
13
+ # * Redistributions of source code must retain the above copyright notice,
14
+ # this list of conditions and the following disclaimer.
15
+ # * Redistributions in binary form must reproduce the above copyright notice,
16
+ # this list of conditions and the following disclaimer in the documentation
17
+ # and/or other materials provided with the distribution.
18
+ # * Neither the name of the copyright holder, nor the names of any other
19
+ # contributors to this software, may be used to endorse or promote products
20
+ # derived from this software without specific prior written permission.
21
+ #
22
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
+ # POSSIBILITY OF SUCH DAMAGE.
33
+ # -----------------------------------------------------------------------------
34
+ ;
35
+
36
+
37
+ # :stopdoc:
38
+
39
+ module Arel
40
+ module Visitors
41
+
42
+ class PostGIS < PostgreSQL
43
+
44
+ FUNC_MAP = {
45
+ 'st_wkttosql' => 'ST_GeomFromEWKT',
46
+ }
47
+
48
+ include ::RGeo::ActiveRecord::SpatialToSql
49
+
50
+ def st_func(standard_name_)
51
+ FUNC_MAP[standard_name_.downcase] || standard_name_
52
+ end
53
+
54
+ alias_method :visit_in_spatial_context, :visit
55
+
56
+ end
57
+
58
+ VISITORS['postgis'] = ::Arel::Visitors::PostGIS
59
+
60
+ end
61
+ end
62
+
63
+ # :startdoc:
@@ -64,7 +64,7 @@ def create_database(config_)
64
64
  ::ActiveRecord::Base.establish_connection(config_)
65
65
  rescue ::Exception => e_
66
66
  $stderr.puts(e_, *(e_.backtrace))
67
- $stderr.puts("Couldn't create database for #{config.inspect}")
67
+ $stderr.puts("Couldn't create database for #{config_.inspect}")
68
68
  end
69
69
  else
70
70
  create_database_without_postgis(config_)
@@ -0,0 +1,280 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # PostGIS adapter for ActiveRecord
4
+ #
5
+ # -----------------------------------------------------------------------------
6
+ # Copyright 2010 Daniel Azuma
7
+ #
8
+ # All rights reserved.
9
+ #
10
+ # Redistribution and use in source and binary forms, with or without
11
+ # modification, are permitted provided that the following conditions are met:
12
+ #
13
+ # * Redistributions of source code must retain the above copyright notice,
14
+ # this list of conditions and the following disclaimer.
15
+ # * Redistributions in binary form must reproduce the above copyright notice,
16
+ # this list of conditions and the following disclaimer in the documentation
17
+ # and/or other materials provided with the distribution.
18
+ # * Neither the name of the copyright holder, nor the names of any other
19
+ # contributors to this software, may be used to endorse or promote products
20
+ # derived from this software without specific prior written permission.
21
+ #
22
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
+ # POSSIBILITY OF SUCH DAMAGE.
33
+ # -----------------------------------------------------------------------------
34
+ ;
35
+
36
+
37
+ # :stopdoc:
38
+
39
+ module ActiveRecord
40
+
41
+ module ConnectionAdapters
42
+
43
+ module PostGISAdapter
44
+
45
+
46
+ class MainAdapter < PostgreSQLAdapter
47
+
48
+
49
+ SPATIAL_COLUMN_CONSTRUCTORS = ::RGeo::ActiveRecord::DEFAULT_SPATIAL_COLUMN_CONSTRUCTORS.merge(
50
+ :geography => {:type => 'geometry', :geographic => true},
51
+ )
52
+
53
+ @@native_database_types = nil
54
+
55
+
56
+ def adapter_name
57
+ PostGISAdapter::ADAPTER_NAME
58
+ end
59
+
60
+
61
+ def spatial_column_constructor(name_)
62
+ SPATIAL_COLUMN_CONSTRUCTORS[name_]
63
+ end
64
+
65
+
66
+ def native_database_types
67
+ @@native_database_types ||= super.merge(:spatial => {:name => 'geometry'})
68
+ end
69
+
70
+
71
+ def postgis_lib_version
72
+ unless defined?(@postgis_lib_version)
73
+ @postgis_lib_version = select_value("SELECT PostGIS_Lib_Version()") rescue nil
74
+ end
75
+ @postgis_lib_version
76
+ end
77
+
78
+
79
+ def srs_database_columns
80
+ {:srtext_column => 'srtext', :proj4text_column => 'proj4text', :auth_name_column => 'auth_name', :auth_srid_column => 'auth_srid'}
81
+ end
82
+
83
+
84
+ def quote(value_, column_=nil)
85
+ if ::RGeo::Feature::Geometry.check_type(value_)
86
+ "'#{::RGeo::WKRep::WKBGenerator.new(:hex_format => true, :type_format => :ewkb, :emit_ewkb_srid => true).generate(value_)}'"
87
+ else
88
+ super
89
+ end
90
+ end
91
+
92
+
93
+ def columns(table_name_, name_=nil)
94
+ table_name_ = table_name_.to_s
95
+ spatial_info_ = spatial_column_info(table_name_)
96
+ column_definitions(table_name_).collect do |name_, type_, default_, notnull_|
97
+ SpatialColumn.new(name_, default_, type_, notnull_ == 'f', type_ =~ /geometry/i ? spatial_info_[name_] : nil)
98
+ end
99
+ end
100
+
101
+
102
+ def indexes(table_name_, name_=nil)
103
+
104
+ # Remove postgis from schemas
105
+ schemas_ = schema_search_path.split(/,/)
106
+ schemas_.delete('postgis')
107
+ schemas_ = schemas_.map{ |p_| quote(p_) }.join(',')
108
+
109
+ # Get index type by joining with pg_am.
110
+ result_ = query(<<-SQL, name_)
111
+ SELECT distinct i.relname, d.indisunique, d.indkey, t.oid, am.amname
112
+ FROM pg_class t, pg_class i, pg_index d, pg_am am
113
+ WHERE i.relkind = 'i'
114
+ AND d.indexrelid = i.oid
115
+ AND d.indisprimary = 'f'
116
+ AND t.oid = d.indrelid
117
+ AND t.relname = '#{table_name_}'
118
+ AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname IN (#{schemas_}) )
119
+ AND i.relam = am.oid
120
+ ORDER BY i.relname
121
+ SQL
122
+
123
+ result_.map do |row_|
124
+ index_name_ = row_[0]
125
+ unique_ = row_[1] == 't'
126
+ indkey_ = row_[2].split(" ")
127
+ oid_ = row_[3]
128
+ indtype_ = row_[4]
129
+
130
+ columns_ = query(<<-SQL, "Columns for index #{row_[0]} on #{table_name_}").inject({}){ |h_, r_| h_[r_[0]] = [r_[1], r_[2]]; h_ }
131
+ SELECT a.attnum, a.attname, t.typname
132
+ FROM pg_attribute a, pg_type t
133
+ WHERE a.attrelid = #{oid_}
134
+ AND a.attnum IN (#{indkey_.join(",")})
135
+ AND a.atttypid = t.oid
136
+ SQL
137
+
138
+ spatial_ = indtype_ == 'gist' && columns_.size == 1 && (columns_.values.first[1] == 'geometry' || columns_.values.first[1] == 'geography')
139
+ column_names_ = columns_.values_at(*indkey_).compact.map{ |a_| a_[0] }
140
+ column_names_.empty? ? nil : ::RGeo::ActiveRecord::SpatialIndexDefinition.new(table_name_, index_name_, unique_, column_names_, nil, spatial_)
141
+ end.compact
142
+ end
143
+
144
+
145
+ def create_table(table_name_, options_={})
146
+ table_name_ = table_name_.to_s
147
+ table_definition_ = SpatialTableDefinition.new(self)
148
+ table_definition_.primary_key(options_[:primary_key] || ::ActiveRecord::Base.get_primary_key(table_name_.singularize)) unless options_[:id] == false
149
+ yield table_definition_ if block_given?
150
+ if options_[:force] && table_exists?(table_name_)
151
+ drop_table(table_name_, options_)
152
+ end
153
+
154
+ create_sql_ = "CREATE#{' TEMPORARY' if options_[:temporary]} TABLE "
155
+ create_sql_ << "#{quote_table_name(table_name_)} ("
156
+ create_sql_ << table_definition_.to_sql
157
+ create_sql_ << ") #{options_[:options]}"
158
+ execute create_sql_
159
+
160
+ table_definition_.non_geographic_spatial_columns.each do |col_|
161
+ type_ = col_.spatial_type.gsub('_', '').upcase
162
+ has_z_ = col_.has_z?
163
+ has_m_ = col_.has_m?
164
+ type_ = "#{type_}M" if has_m_ && !has_z_
165
+ dimensions_ = 2
166
+ dimensions_ += 1 if has_z_
167
+ dimensions_ += 1 if has_m_
168
+ execute("SELECT AddGeometryColumn('#{quote_string(table_name_)}', '#{quote_string(col_.name.to_s)}', #{col_.srid}, '#{quote_string(type_)}', #{dimensions_})")
169
+ end
170
+ end
171
+
172
+
173
+ def drop_table(table_name_, options_={})
174
+ execute("DELETE from geometry_columns where f_table_name='#{quote_string(table_name_.to_s)}'")
175
+ super
176
+ end
177
+
178
+
179
+ def add_column(table_name_, column_name_, type_, options_={})
180
+ table_name_ = table_name_.to_s
181
+ if (info_ = spatial_column_constructor(type_.to_sym))
182
+ limit_ = options_[:limit]
183
+ options_.merge!(limit_) if limit_.is_a?(::Hash)
184
+ type_ = (options_[:type] || info_[:type] || type_).to_s.gsub('_', '').upcase
185
+ has_z_ = options_[:has_z]
186
+ has_m_ = options_[:has_m]
187
+ srid_ = (options_[:srid] || 4326).to_i
188
+ if options_[:geographic]
189
+ type_ << 'Z' if has_z_
190
+ type_ << 'M' if has_m_
191
+ execute("ALTER TABLE #{quote_table_name(table_name_)} ADD COLUMN #{quote_column_name(column_name_)} GEOGRAPHY(#{type_},#{srid_})")
192
+ change_column_default(table_name_, column_name_, options_[:default]) if options_include_default?(options_)
193
+ change_column_null(table_name_, column_name_, false, options_[:default]) if options_[:null] == false
194
+ else
195
+ type_ = "#{type_}M" if has_m_ && !has_z_
196
+ dimensions_ = 2
197
+ dimensions_ += 1 if has_z_
198
+ dimensions_ += 1 if has_m_
199
+ execute("SELECT AddGeometryColumn('#{quote_string(table_name_)}', '#{quote_string(column_name_.to_s)}', #{srid_}, '#{quote_string(type_)}', #{dimensions_})")
200
+ end
201
+ else
202
+ super
203
+ end
204
+ end
205
+
206
+
207
+ def remove_column(table_name_, *column_names_)
208
+ column_names_ = column_names_.flatten.map{ |n_| n_.to_s }
209
+ spatial_info_ = spatial_column_info(table_name_)
210
+ remaining_column_names_ = []
211
+ column_names_.each do |name_|
212
+ if spatial_info_.include?(name_)
213
+ execute("SELECT DropGeometryColumn('#{quote_string(table_name_.to_s)}','#{quote_string(name_)}')")
214
+ else
215
+ remaining_column_names_ << name_.to_sym
216
+ end
217
+ end
218
+ if remaining_column_names_.size > 0
219
+ super(table_name_, *remaining_column_names_)
220
+ end
221
+ end
222
+
223
+
224
+ def add_index(table_name_, column_name_, options_={})
225
+ table_name_ = table_name_.to_s
226
+ column_names_ = ::Array.wrap(column_name_)
227
+ index_name_ = index_name(table_name_, :column => column_names_)
228
+ gist_clause_ = ''
229
+ index_type_ = ''
230
+ if ::Hash === options_ # legacy support, since this param was a string
231
+ index_type_ = 'UNIQUE' if options_[:unique]
232
+ index_name_ = options_[:name].to_s if options_.key?(:name)
233
+ gist_clause_ = 'USING GIST' if options_[:spatial]
234
+ else
235
+ index_type_ = options_
236
+ end
237
+ if index_name_.length > index_name_length
238
+ raise ::ArgumentError, "Index name '#{index_name_}' on table '#{table_name_}' is too long; the limit is #{index_name_length} characters"
239
+ end
240
+ if index_name_exists?(table_name_, index_name_, false)
241
+ raise ::ArgumentError, "Index name '#{index_name_}' on table '#{table_name_}' already exists"
242
+ end
243
+ quoted_column_names_ = quoted_columns_for_index(column_names_, options_).join(", ")
244
+ execute "CREATE #{index_type_} INDEX #{quote_column_name(index_name_)} ON #{quote_table_name(table_name_)} #{gist_clause_} (#{quoted_column_names_})"
245
+ end
246
+
247
+
248
+ def spatial_column_info(table_name_)
249
+ info_ = query("SELECT * FROM geometry_columns WHERE f_table_name='#{quote_string(table_name_.to_s)}'")
250
+ result_ = {}
251
+ info_.each do |row_|
252
+ name_ = row_[3]
253
+ type_ = row_[6]
254
+ dimension_ = row_[4].to_i
255
+ has_m_ = type_ =~ /m$/i ? true : false
256
+ type_.sub!(/m$/, '')
257
+ has_z_ = dimension_ > 3 || dimension_ == 3 && !has_m_
258
+ result_[name_] = {
259
+ :name => name_,
260
+ :type => type_,
261
+ :dimension => dimension_,
262
+ :srid => row_[5].to_i,
263
+ :has_z => has_z_,
264
+ :has_m => has_m_,
265
+ }
266
+ end
267
+ result_
268
+ end
269
+
270
+
271
+ end
272
+
273
+
274
+ end
275
+
276
+ end
277
+
278
+ end
279
+
280
+ # :startdoc:
@@ -0,0 +1,64 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # Railtie for PostGIS adapter
4
+ #
5
+ # -----------------------------------------------------------------------------
6
+ # Copyright 2010 Daniel Azuma
7
+ #
8
+ # All rights reserved.
9
+ #
10
+ # Redistribution and use in source and binary forms, with or without
11
+ # modification, are permitted provided that the following conditions are met:
12
+ #
13
+ # * Redistributions of source code must retain the above copyright notice,
14
+ # this list of conditions and the following disclaimer.
15
+ # * Redistributions in binary form must reproduce the above copyright notice,
16
+ # this list of conditions and the following disclaimer in the documentation
17
+ # and/or other materials provided with the distribution.
18
+ # * Neither the name of the copyright holder, nor the names of any other
19
+ # contributors to this software, may be used to endorse or promote products
20
+ # derived from this software without specific prior written permission.
21
+ #
22
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
+ # POSSIBILITY OF SUCH DAMAGE.
33
+ # -----------------------------------------------------------------------------
34
+ ;
35
+
36
+
37
+ require 'rails/railtie'
38
+
39
+
40
+ # :stopdoc:
41
+
42
+ module ActiveRecord
43
+
44
+ module ConnectionAdapters
45
+
46
+ module PostGISAdapter
47
+
48
+
49
+ class Railtie < ::Rails::Railtie
50
+
51
+ rake_tasks do
52
+ load ::File.expand_path('databases.rake', ::File.dirname(__FILE__))
53
+ end
54
+
55
+ end
56
+
57
+
58
+ end
59
+
60
+ end
61
+
62
+ end
63
+
64
+ # :startdoc:
@@ -0,0 +1,168 @@
1
+ # -----------------------------------------------------------------------------
2
+ #
3
+ # PostGIS adapter for ActiveRecord
4
+ #
5
+ # -----------------------------------------------------------------------------
6
+ # Copyright 2010 Daniel Azuma
7
+ #
8
+ # All rights reserved.
9
+ #
10
+ # Redistribution and use in source and binary forms, with or without
11
+ # modification, are permitted provided that the following conditions are met:
12
+ #
13
+ # * Redistributions of source code must retain the above copyright notice,
14
+ # this list of conditions and the following disclaimer.
15
+ # * Redistributions in binary form must reproduce the above copyright notice,
16
+ # this list of conditions and the following disclaimer in the documentation
17
+ # and/or other materials provided with the distribution.
18
+ # * Neither the name of the copyright holder, nor the names of any other
19
+ # contributors to this software, may be used to endorse or promote products
20
+ # derived from this software without specific prior written permission.
21
+ #
22
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
+ # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26
+ # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27
+ # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28
+ # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29
+ # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30
+ # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
+ # POSSIBILITY OF SUCH DAMAGE.
33
+ # -----------------------------------------------------------------------------
34
+ ;
35
+
36
+
37
+ # :stopdoc:
38
+
39
+ module ActiveRecord
40
+
41
+ module ConnectionAdapters
42
+
43
+ module PostGISAdapter
44
+
45
+
46
+ class SpatialColumn < ConnectionAdapters::PostgreSQLColumn
47
+
48
+
49
+ def initialize(name_, default_, sql_type_=nil, null_=true, opts_=nil)
50
+ super(name_, default_, sql_type_, null_)
51
+ @geographic = sql_type_ =~ /geography/i ? true : false
52
+ if opts_
53
+ @geometric_type = ::RGeo::ActiveRecord.geometric_type_from_name(opts_[:type])
54
+ @srid = opts_[:srid].to_i
55
+ @has_z = opts_[:has_z]
56
+ @has_m = opts_[:has_m]
57
+ elsif @geographic
58
+ @geometric_type = ::RGeo::Feature::Geometry
59
+ @srid = 4326
60
+ @has_z = @has_m = false
61
+ if sql_type_ =~ /geography\((.*)\)$/i
62
+ params_ = $1.split(',')
63
+ if params_.size >= 2
64
+ if params_.first =~ /([a-z]+[^zm])(z?)(m?)/i
65
+ @has_z = $2.length > 0
66
+ @has_m = $3.length > 0
67
+ @geometric_type = ::RGeo::ActiveRecord.geometric_type_from_name($1)
68
+ end
69
+ if params_.last =~ /(\d+)/
70
+ @srid = $1.to_i
71
+ end
72
+ end
73
+ end
74
+ else
75
+ @geometric_type = @has_z = @has_m = nil
76
+ @srid = 0
77
+ end
78
+ if type == :spatial
79
+ @limit = {:srid => @srid, :type => @geometric_type.type_name.underscore}
80
+ @limit[:has_z] = true if @has_z
81
+ @limit[:has_m] = true if @has_m
82
+ @limit[:geographic] = true if @geographic
83
+ end
84
+ @ar_class = ::ActiveRecord::Base
85
+ end
86
+
87
+
88
+ def set_ar_class(val_)
89
+ @ar_class = val_
90
+ end
91
+
92
+
93
+ attr_reader :geographic
94
+ attr_reader :srid
95
+ attr_reader :geometric_type
96
+ attr_reader :has_z
97
+ attr_reader :has_m
98
+
99
+ alias_method :geographic?, :geographic
100
+ alias_method :has_z?, :has_z
101
+ alias_method :has_m?, :has_m
102
+
103
+
104
+ def spatial?
105
+ type == :spatial
106
+ end
107
+
108
+
109
+ def klass
110
+ type == :spatial ? ::RGeo::Feature::Geometry : super
111
+ end
112
+
113
+
114
+ def type_cast(value_)
115
+ type == :spatial ? SpatialColumn.convert_to_geometry(value_, @ar_class, name, @geographic, @srid, @has_z, @has_m) : super
116
+ end
117
+
118
+
119
+ def type_cast_code(var_name_)
120
+ type == :spatial ? "::ActiveRecord::ConnectionAdapters::PostGISAdapter::SpatialColumn.convert_to_geometry(#{var_name_}, self.class, #{name.inspect}, #{@geographic ? 'true' : 'false'}, #{@srid.inspect}, #{@has_z ? 'true' : 'false'}, #{@has_m ? 'true' : 'false'})" : super
121
+ end
122
+
123
+
124
+ private
125
+
126
+
127
+ def simplified_type(sql_type_)
128
+ sql_type_ =~ /geography|geometry|point|linestring|polygon/i ? :spatial : super
129
+ end
130
+
131
+
132
+ def self.convert_to_geometry(input_, ar_class_, column_, geographic_, srid_, has_z_, has_m_)
133
+ case input_
134
+ when ::RGeo::Feature::Geometry
135
+ factory_ = ar_class_.rgeo_factory_for_column(column_, :srid => srid_, :has_z_coordinate => has_z_, :has_m_coordinate => has_m_, :geographic => geographic_)
136
+ ::RGeo::Feature.cast(input_, factory_)
137
+ when ::String
138
+ if input_.length == 0
139
+ nil
140
+ else
141
+ factory_ = ar_class_.rgeo_factory_for_column(column_, :srid => srid_, :has_z_coordinate => has_z_, :has_m_coordinate => has_m_, :geographic => geographic_)
142
+ marker_ = input_[0,1]
143
+ if marker_ == "\x00" || marker_ == "\x01"
144
+ ::RGeo::WKRep::WKBParser.new(factory_, :support_ewkb => true).parse(input_) rescue nil
145
+ elsif input_[0,4] =~ /[0-9a-fA-F]{4}/
146
+ ::RGeo::WKRep::WKBParser.new(factory_, :support_ewkb => true).parse_hex(input_) rescue nil
147
+ else
148
+ ::RGeo::WKRep::WKTParser.new(factory_, :support_ewkt => true).parse(input_) rescue nil
149
+ end
150
+ end
151
+ else
152
+ nil
153
+ end
154
+ end
155
+
156
+
157
+ end
158
+
159
+
160
+ end
161
+
162
+
163
+ end
164
+
165
+
166
+ end
167
+
168
+ # :startdoc: