spatial_adapter 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/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