spatial_adapter 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2006 Guilhem Vellut <guilhem.vellut+georuby@gmail.com>
2
+ Copyright (c) 2010 Pete Deffendol <pete@fragility.us>
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,178 @@
1
+ = Spatial Adapter for ActiveRecord
2
+
3
+ This is the Spatial Adapter for ActiveRecord. It enhances ActiveRecord to
4
+ handle spatial datatypes in the following databases:
5
+
6
+ - PostgreSQL (using PostGIS)
7
+ - MySQL (using Spatial Extensions)
8
+
9
+ == Dependencies
10
+
11
+ The following gems are required:
12
+
13
+ - GeoRuby
14
+ - ActiveRecord (version 2.2.2 and up)
15
+
16
+ For PostgreSQL:
17
+
18
+ - PostGIS version 1.4.0 or higher should be installed in your database
19
+
20
+ == Installation
21
+
22
+ Choose ONE of the following installation methods. You shouldn't have to do both.
23
+
24
+ === From RubyGems
25
+
26
+ This is the preferred method of installation, and will pull in the required
27
+ dependencies as well.
28
+
29
+ gem install spatial_adapter
30
+
31
+ In your Rails app, you can add a gem dependency in environment.rb:
32
+
33
+ config.gem "spatial_adapter"
34
+
35
+ === As a Rails Plugin
36
+
37
+ In your Rails project, run the following:
38
+
39
+ script/plugin install git://github.com/fragility/spatial_adapter.git
40
+
41
+ You need to have Git installed first.
42
+
43
+ == Operations
44
+
45
+ Geometric columns in your ActiveRecord models now appear just like any other
46
+ column of other basic data types. They can also be dumped in ruby schema mode
47
+ and loaded in migrations the same way as columns of basic types.
48
+
49
+ === Migrations
50
+
51
+ Here is an example of code for the creation of a table with a geometric column
52
+ in PostGIS, along with the addition of a spatial index on the column:
53
+
54
+ ActiveRecord::Schema.define do
55
+ create_table :table_points, :force => true do |t|
56
+ t.string :data
57
+ t.point :geom, :null => false, :srid => 123, :with_z => true
58
+ end
59
+
60
+ add_index :table_points, :geom, :spatial => true
61
+ end
62
+
63
+ Here is a related statement valid for MySql version <= 5.0.16:
64
+
65
+ ActiveRecord::Schema.define do
66
+ create_table "table_points", ;options=>"ENGINE=MyISAM", :force => true do |t|
67
+ t.string :data
68
+ t.point :geom, :null => false
69
+ end
70
+
71
+ add_index :table_points, :geom, :spatial => true
72
+ end
73
+
74
+ === Differences Between Databases
75
+
76
+ - On all versions of MySQL, the :srid, :with_z, and :with_m options are ignored, since
77
+ they are not supported.
78
+
79
+ - On MySQL versions <= 5.0.16, you have to add <tt>:options =>
80
+ "ENGINE=MyISAM"</tt> to the create_table statement, since only MyISAM tables
81
+ can have spatial columns. In addition, only MyISAM tables may have spatial
82
+ indexes.
83
+
84
+ === Models
85
+
86
+ Create your ActiveRecord models normally. Spatial Adapter will automatically
87
+ handle spatial columns, converting them to the appropriate GeoRuby type.
88
+
89
+ class TablePoint < ActiveRecord::Base
90
+ end
91
+
92
+ === Access
93
+
94
+ Here is an example of row creation and access, using the model and the table
95
+ defined above:
96
+
97
+ pt = TablePoint.new(
98
+ :data => "Hello!",
99
+ :geom => Point.from_x_y_z(-1.6, 2.8, -3.4, 123))
100
+ pt.save
101
+ pt = TablePoint.find_first
102
+ puts pt.geom.x #access the geom column like any other
103
+
104
+ === Fixtures
105
+
106
+ If you use fixtures for your unit tests, at some point, you will want to input
107
+ a geometry. You could transform your geometries to a form suitable for YAML
108
+ yourself every time but Spatial Adapter provides a method to do it for you:
109
+ +to_fixture_format+. You would use it like this, if the geometric column is a
110
+ point:
111
+
112
+ fixture:
113
+ id: 1
114
+ data: HELLO
115
+ geom: <%= Point.from_x_y(123.5,321.9).to_fixture_format %>
116
+
117
+ === Finder Enhancements
118
+
119
+ Enhancements to find_by_* and friends has been removed from this version of
120
+ Spatial Adapter until a cleaner implementation can be made. (The previous
121
+ implementation made adapter-specific modifications to ActiveRecord::Base,
122
+ which prevented multiple adapters from being loaded at once.)
123
+
124
+ === Geometric data types
125
+
126
+ Ruby geometric datatypes are currently made available only through the GeoRuby
127
+ library (http://georuby.rubyforge.org/): This is where the
128
+ <tt>Point.from_x_y</tt> in the example above comes from.
129
+
130
+ == Warning
131
+
132
+ - Since ActiveRecord seems to keep only the string values directly returned
133
+ from the database, it translates from these to the correct types everytime
134
+ an attribute is read, which is probably ok for simple types, but might be
135
+ less than efficient for geometries, since the EWKB string has to be parsed
136
+ everytime. Also it means you cannot modify the geometry object returned from
137
+ an attribute directly:
138
+
139
+ place = Place.find_first
140
+ place.the_geom.y=123456.7 # this doesn't work
141
+
142
+ Since the translation to a geometry is performed every time the_geom is read,
143
+ the change to y will not be saved! You would have to do something like this:
144
+
145
+ place = Place.find_first
146
+ the_geom = place.the_geom
147
+ the_geom.y=123456.7
148
+ place.the_geom = the_geom
149
+
150
+ == License
151
+
152
+ The Spatial Adapter for ActiveRecord is released under the MIT license.
153
+
154
+ == Latest Changes
155
+
156
+ Spatial Adapter has been refactored and is now available as a Ruby gem. The
157
+ dependency on Rails has been removed. Unfortunately, the current version is
158
+ without some of the previous functionality, until a cleaner implementation is
159
+ made.
160
+
161
+ The previous release is available on the "legacy" branch.
162
+
163
+ === Removed Features in 0.2.0
164
+
165
+ - Compatibility with ActiveRecord/Rails older than version 2.2.2
166
+ - enhancements to find_by_* for spatial columns
167
+ - to_fixture_format extension to the GeoRuby types
168
+
169
+ These will hopefully be added back in the near future.
170
+
171
+ == TODO
172
+
173
+ - Add support for PostGIS geography types (in PostGIS 1.5.0+)
174
+
175
+ == Support
176
+
177
+ Any questions, enhancement proposals, bug notifications or corrections can be
178
+ made via the project page at http://github.com/fragility/spatial_adapter
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.0
@@ -0,0 +1,26 @@
1
+ require 'geo_ruby'
2
+ require 'active_record'
3
+
4
+ module SpatialAdapter
5
+ #Translation of geometric data types
6
+ def geometry_data_types
7
+ {
8
+ :point => { :name => "POINT" },
9
+ :line_string => { :name => "LINESTRING" },
10
+ :polygon => { :name => "POLYGON" },
11
+ :geometry_collection => { :name => "GEOMETRYCOLLECTION" },
12
+ :multi_point => { :name => "MULTIPOINT" },
13
+ :multi_line_string => { :name => "MULTILINESTRING" },
14
+ :multi_polygon => { :name => "MULTIPOLYGON" },
15
+ :geometry => { :name => "GEOMETRY"}
16
+ }
17
+ end
18
+ end
19
+
20
+ require 'spatial_adapter/raw_geom_info'
21
+ require 'spatial_adapter/spatial_column'
22
+ require 'spatial_adapter/schema_definitions'
23
+ require 'spatial_adapter/schema_dumper'
24
+ require 'spatial_adapter/table_definition'
25
+ require 'spatial_adapter/adapters/postgis'
26
+ require 'spatial_adapter/adapters/mysql'
@@ -0,0 +1,101 @@
1
+ require 'active_record/connection_adapters/mysql_adapter'
2
+
3
+ ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do
4
+ include SpatialAdapter
5
+
6
+ def supports_geography?
7
+ false
8
+ end
9
+
10
+ alias :original_native_database_types :native_database_types
11
+ def native_database_types
12
+ original_native_database_types.merge!(geometry_data_types)
13
+ end
14
+
15
+ alias :original_quote :quote
16
+ #Redefines the quote method to add behaviour for when a Geometry is encountered ; used when binding variables in find_by methods
17
+ def quote(value, column = nil)
18
+ if value.kind_of?(GeoRuby::SimpleFeatures::Geometry)
19
+ "GeomFromWKB(0x#{value.as_hex_wkb},#{value.srid})"
20
+ else
21
+ original_quote(value,column)
22
+ end
23
+ end
24
+
25
+ #Redefinition of columns to add the information that a column is geometric
26
+ def columns(table_name, name = nil)#:nodoc:
27
+ sql = "SHOW FIELDS FROM #{quote_table_name(table_name)}"
28
+ columns = []
29
+ result = execute(sql, name)
30
+ result.each do |field|
31
+ if field[1] =~ /geometry|point|linestring|polygon|multipoint|multilinestring|multipolygon|geometrycollection/i
32
+ #to note that the column is spatial
33
+ columns << ActiveRecord::ConnectionAdapters::SpatialMysqlColumn.new(field[0], field[4], field[1], field[2] == "YES")
34
+ else
35
+ columns << ActiveRecord::ConnectionAdapters::MysqlColumn.new(field[0], field[4], field[1], field[2] == "YES")
36
+ end
37
+ end
38
+ result.free
39
+ columns
40
+ end
41
+
42
+
43
+ #operations relative to migrations
44
+
45
+ #Redefines add_index to support the case where the index is spatial
46
+ #If the :spatial key in the options table is true, then the sql string for a spatial index is created
47
+ def add_index(table_name,column_name,options = {})
48
+ index_name = options[:name] || index_name(table_name,:column => Array(column_name))
49
+
50
+ if options[:spatial]
51
+ execute "CREATE SPATIAL INDEX #{index_name} ON #{table_name} (#{Array(column_name).join(", ")})"
52
+ else
53
+ super
54
+ end
55
+ end
56
+
57
+ #Check the nature of the index : If it is SPATIAL, it is indicated in the IndexDefinition object (redefined to add the spatial flag in spatial_adapter_common.rb)
58
+ def indexes(table_name, name = nil)#:nodoc:
59
+ indexes = []
60
+ current_index = nil
61
+ execute("SHOW KEYS FROM #{table_name}", name).each do |row|
62
+ if current_index != row[2]
63
+ next if row[2] == "PRIMARY" # skip the primary key
64
+ current_index = row[2]
65
+ indexes << ActiveRecord::ConnectionAdapters::IndexDefinition.new(row[0], row[2], row[1] == "0", [], row[10] == "SPATIAL")
66
+ end
67
+ indexes.last.columns << row[4]
68
+ end
69
+ indexes
70
+ end
71
+
72
+ #Get the table creation options : Only the engine for now. The text encoding could also be parsed and returned here.
73
+ def options_for(table)
74
+ result = execute("show table status like '#{table}'")
75
+ engine = result.fetch_row[1]
76
+ if engine !~ /inno/i #inno is default so do nothing for it in order not to clutter the migration
77
+ "ENGINE=#{engine}"
78
+ else
79
+ nil
80
+ end
81
+ end
82
+ end
83
+
84
+
85
+ module ActiveRecord
86
+ module ConnectionAdapters
87
+ class SpatialMysqlColumn < MysqlColumn
88
+ include SpatialAdapter::SpatialColumn
89
+
90
+ #MySql-specific geometry string parsing. By default, MySql returns geometries in strict wkb format with "0" characters in the first 4 positions.
91
+ def self.string_to_geometry(string)
92
+ return string unless string.is_a?(String)
93
+ begin
94
+ GeoRuby::SimpleFeatures::Geometry.from_ewkb(string[4..-1])
95
+ rescue Exception => exception
96
+ nil
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,302 @@
1
+ require 'active_record/connection_adapters/postgresql_adapter'
2
+
3
+ ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do
4
+ include SpatialAdapter
5
+
6
+ def postgis_version
7
+ begin
8
+ select_value("SELECT postgis_full_version()").scan(/POSTGIS="([\d\.]*)"/)[0][0]
9
+ rescue ActiveRecord::StatementInvalid
10
+ nil
11
+ end
12
+ end
13
+
14
+ def postgis_major_version
15
+ version = postgis_version
16
+ version ? version.scan(/^(\d)\.\d\.\d$/)[0][0].to_i : nil
17
+ end
18
+
19
+ def postgis_minor_version
20
+ version = postgis_version
21
+ version ? version.scan(/^\d\.(\d)\.\d$/)[0][0].to_i : nil
22
+ end
23
+
24
+ def spatial?
25
+ !postgis_version.nil?
26
+ end
27
+
28
+ def supports_geography?
29
+ postgis_major_version > 1 || (postgis_major_version == 1 && postgis_minor_version >= 5)
30
+ end
31
+
32
+ alias :original_native_database_types :native_database_types
33
+ def native_database_types
34
+ original_native_database_types.merge!(geometry_data_types)
35
+ end
36
+
37
+ alias :original_quote :quote
38
+ #Redefines the quote method to add behaviour for when a Geometry is encountered
39
+ def quote(value, column = nil)
40
+ if value.kind_of?(GeoRuby::SimpleFeatures::Geometry)
41
+ "'#{value.as_hex_ewkb}'"
42
+ else
43
+ original_quote(value,column)
44
+ end
45
+ end
46
+
47
+ def columns(table_name, name = nil) #:nodoc:
48
+ raw_geom_infos = column_spatial_info(table_name)
49
+
50
+ column_definitions(table_name).collect do |name, type, default, notnull|
51
+ case type
52
+ when /geometry/i
53
+ raw_geom_info = raw_geom_infos[name]
54
+ if raw_geom_info.nil?
55
+ ActiveRecord::ConnectionAdapters::SpatialPostgreSQLColumn.create_simplified(name, default, notnull == "f")
56
+ else
57
+ ActiveRecord::ConnectionAdapters::SpatialPostgreSQLColumn.new(name, default, raw_geom_info.type, notnull == "f", raw_geom_info.srid, raw_geom_info.with_z, raw_geom_info.with_m)
58
+ end
59
+ else
60
+ ActiveRecord::ConnectionAdapters::PostgreSQLColumn.new(name, default, type, notnull == "f")
61
+ end
62
+ end
63
+ end
64
+
65
+ def create_table(table_name, options = {})
66
+ # Using the subclassed table definition
67
+ table_definition = ActiveRecord::ConnectionAdapters::PostgreSQLTableDefinition.new(self)
68
+ table_definition.primary_key(options[:primary_key] || ActiveRecord::Base.get_primary_key(table_name.to_s.singularize)) unless options[:id] == false
69
+
70
+ yield table_definition if block_given?
71
+
72
+ if options[:force] && table_exists?(table_name)
73
+ drop_table(table_name, options)
74
+ end
75
+
76
+ create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
77
+ create_sql << "#{quote_table_name(table_name)} ("
78
+ create_sql << table_definition.to_sql
79
+ create_sql << ") #{options[:options]}"
80
+ execute create_sql
81
+
82
+ # This is the additional portion for PostGIS
83
+ unless table_definition.geom_columns.nil?
84
+ table_definition.geom_columns.each do |geom_column|
85
+ execute geom_column.to_sql(table_name)
86
+ end
87
+ end
88
+ end
89
+
90
+ alias :original_remove_column :remove_column
91
+ def remove_column(table_name, column_name, options = {})
92
+ columns(table_name).each do |col|
93
+ if col.name == column_name.to_s
94
+ #check if the column is geometric
95
+ unless geometry_data_types[col.type].nil? or
96
+ (options[:remove_using_dropgeometrycolumn] == false)
97
+ execute "SELECT DropGeometryColumn('#{table_name}','#{column_name}')"
98
+ else
99
+ original_remove_column(table_name, column_name)
100
+ end
101
+ end
102
+ end
103
+ end
104
+
105
+ alias :original_add_column :add_column
106
+ def add_column(table_name, column_name, type, options = {})
107
+ unless geometry_data_types[type].nil? or (options[:create_using_addgeometrycolumn] == false)
108
+ geom_column = ActiveRecord::ConnectionAdapters::PostgreSQLColumnDefinition.new(self, column_name, type, nil, nil, options[:null], options[:srid] || -1 , options[:with_z] || false , options[:with_m] || false)
109
+ execute geom_column.to_sql(table_name)
110
+ else
111
+ original_add_column(table_name, column_name, type, options)
112
+ end
113
+ end
114
+
115
+ # Adds an index to a column.
116
+ def add_index(table_name, column_name, options = {})
117
+ column_names = Array(column_name)
118
+ index_name = index_name(table_name, :column => column_names)
119
+
120
+ if Hash === options # legacy support, since this param was a string
121
+ index_type = options[:unique] ? "UNIQUE" : ""
122
+ index_name = options[:name] || index_name
123
+ index_method = options[:spatial] ? 'USING GIST' : ""
124
+ else
125
+ index_type = options
126
+ end
127
+ quoted_column_names = column_names.map { |e| quote_column_name(e) }.join(", ")
128
+ execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_method} (#{quoted_column_names})"
129
+ end
130
+
131
+ # Returns the list of all indexes for a table.
132
+ # This is a full replacement for the ActiveRecord method and as a result
133
+ # has a higher probability of breaking in future releases
134
+ def indexes(table_name, name = nil)
135
+ schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',')
136
+ result = query(<<-SQL, name)
137
+ SELECT i.relname, d.indisunique, d.indkey, t.oid, am.amname
138
+ FROM pg_class t, pg_class i, pg_index d, pg_attribute a, pg_am am
139
+ WHERE i.relkind = 'i'
140
+ AND d.indexrelid = i.oid
141
+ AND d.indisprimary = 'f'
142
+ AND t.oid = d.indrelid
143
+ AND i.relam = am.oid
144
+ AND t.relname = '#{table_name}'
145
+ AND a.attrelid = t.oid
146
+ AND ( d.indkey[0]=a.attnum OR d.indkey[1]=a.attnum
147
+ OR d.indkey[2]=a.attnum OR d.indkey[3]=a.attnum
148
+ OR d.indkey[4]=a.attnum OR d.indkey[5]=a.attnum
149
+ OR d.indkey[6]=a.attnum OR d.indkey[7]=a.attnum
150
+ OR d.indkey[8]=a.attnum OR d.indkey[9]=a.attnum )
151
+ ORDER BY i.relname
152
+ SQL
153
+
154
+ indexes = []
155
+
156
+ indexes = result.map do |row|
157
+ index_name = row[0]
158
+ unique = row[1] == 't'
159
+ indkey = row[2].split(" ")
160
+ oid = row[3]
161
+ spatial = row[4] == "gist"
162
+
163
+ columns = query(<<-SQL, "Columns for index #{row[0]} on #{table_name}").inject({}) {|attlist, r| attlist[r[1]] = r[0]; attlist}
164
+ SELECT a.attname, a.attnum
165
+ FROM pg_attribute a
166
+ WHERE a.attrelid = #{oid}
167
+ AND a.attnum IN (#{indkey.join(",")})
168
+ SQL
169
+
170
+ column_names = indkey.map {|attnum| columns[attnum] }
171
+ ActiveRecord::ConnectionAdapters::IndexDefinition.new(table_name, index_name, unique, column_names, spatial)
172
+ end
173
+
174
+ indexes
175
+ end
176
+
177
+ def disable_referential_integrity(&block) #:nodoc:
178
+ if supports_disable_referential_integrity?() then
179
+ execute(tables_without_postgis.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
180
+ end
181
+ yield
182
+ ensure
183
+ if supports_disable_referential_integrity?() then
184
+ execute(tables_without_postgis.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";"))
185
+ end
186
+ end
187
+
188
+ private
189
+
190
+ def tables_without_postgis
191
+ tables - %w{ geometry_columns spatial_ref_sys }
192
+ end
193
+
194
+ def column_spatial_info(table_name)
195
+ constr = query("SELECT * FROM geometry_columns WHERE f_table_name = '#{table_name}'")
196
+
197
+ raw_geom_infos = {}
198
+ constr.each do |constr_def_a|
199
+ raw_geom_infos[constr_def_a[3]] ||= SpatialAdapter::RawGeomInfo.new
200
+ raw_geom_infos[constr_def_a[3]].type = constr_def_a[6]
201
+ raw_geom_infos[constr_def_a[3]].dimension = constr_def_a[4].to_i
202
+ raw_geom_infos[constr_def_a[3]].srid = constr_def_a[5].to_i
203
+
204
+ if raw_geom_infos[constr_def_a[3]].type[-1] == ?M
205
+ raw_geom_infos[constr_def_a[3]].with_m = true
206
+ raw_geom_infos[constr_def_a[3]].type.chop!
207
+ else
208
+ raw_geom_infos[constr_def_a[3]].with_m = false
209
+ end
210
+ end
211
+
212
+ raw_geom_infos.each_value do |raw_geom_info|
213
+ #check the presence of z and m
214
+ raw_geom_info.convert!
215
+ end
216
+
217
+ raw_geom_infos
218
+
219
+ end
220
+ end
221
+
222
+ module ActiveRecord
223
+ module ConnectionAdapters
224
+ class PostgreSQLTableDefinition < TableDefinition
225
+ attr_reader :geom_columns
226
+
227
+ def column(name, type, options = {})
228
+ unless (@base.geometry_data_types[type.to_sym].nil? or
229
+ (options[:create_using_addgeometrycolumn] == false))
230
+
231
+ geom_column = PostgreSQLColumnDefinition.new(@base, name, type)
232
+ geom_column.null = options[:null]
233
+ geom_column.srid = options[:srid] || -1
234
+ geom_column.with_z = options[:with_z] || false
235
+ geom_column.with_m = options[:with_m] || false
236
+
237
+ @geom_columns ||= []
238
+ @geom_columns << geom_column
239
+ else
240
+ super(name, type, options)
241
+ end
242
+ end
243
+ end
244
+
245
+ class PostgreSQLColumnDefinition < ColumnDefinition
246
+ attr_accessor :srid, :with_z, :with_m
247
+ attr_reader :spatial
248
+
249
+ def initialize(base = nil, name = nil, type=nil, limit=nil, default=nil, null=nil, srid=-1, with_z=false, with_m=false)
250
+ super(base, name, type, limit, default,null)
251
+ @spatial = true
252
+ @srid = srid
253
+ @with_z = with_z
254
+ @with_m = with_m
255
+ end
256
+
257
+ def to_sql(table_name)
258
+ if @spatial
259
+ type_sql = type_to_sql(type.to_sym)
260
+ type_sql += "M" if with_m and !with_z
261
+ if with_m and with_z
262
+ dimension = 4
263
+ elsif with_m or with_z
264
+ dimension = 3
265
+ else
266
+ dimension = 2
267
+ end
268
+
269
+ column_sql = "SELECT AddGeometryColumn('#{table_name}','#{name}',#{srid},'#{type_sql}',#{dimension})"
270
+ column_sql += ";ALTER TABLE #{table_name} ALTER #{name} SET NOT NULL" if null == false
271
+ column_sql
272
+ else
273
+ super
274
+ end
275
+ end
276
+
277
+ private
278
+
279
+ def type_to_sql(name, limit=nil)
280
+ base.type_to_sql(name, limit) rescue name
281
+ end
282
+ end
283
+ end
284
+ end
285
+
286
+ module ActiveRecord
287
+ module ConnectionAdapters
288
+ class SpatialPostgreSQLColumn < PostgreSQLColumn
289
+ include SpatialAdapter::SpatialColumn
290
+
291
+ #Transforms a string to a geometry. PostGIS returns a HewEWKB string.
292
+ def self.string_to_geometry(string)
293
+ return string unless string.is_a?(String)
294
+ GeoRuby::SimpleFeatures::Geometry.from_hex_ewkb(string) rescue nil
295
+ end
296
+
297
+ def self.create_simplified(name,default,null = true)
298
+ new(name,default,"geometry",null,nil,nil,nil)
299
+ end
300
+ end
301
+ end
302
+ end