activerecord-postgis-adapter 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: