gotime-postgis_adapter 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,174 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # PostGIS Adapter - http://github.com/nofxx/postgis_adapter
4
+ #
5
+ # Hope you enjoy this plugin.
6
+ #
7
+ #
8
+ # Post any bugs/suggestions to GitHub issues tracker:
9
+ # http://github.com/nofxx/postgis_adapter/issues
10
+ #
11
+ #
12
+ # Some links:
13
+ #
14
+ # PostGis Manual - http://postgis.refractions.net/documentation/manual-svn
15
+ # Earth Spheroid - http://en.wikipedia.org/wiki/Figure_of_the_Earth
16
+ #
17
+
18
+ module PostgisAdapter
19
+ module Functions
20
+ # WGS84 Spheroid
21
+ EARTH_SPHEROID = "'SPHEROID[\"GRS-80\",6378137,298.257222101]'" # SRID => 4326
22
+
23
+ def postgis_calculate(operation, subjects, options = {})
24
+ subjects = [subjects] unless subjects.respond_to?(:map)
25
+ execute_geometrical_calculation(operation, subjects, options)
26
+ end
27
+
28
+ def geo_columns
29
+ @geo_columns ||= postgis_geoms[:columns]
30
+ end
31
+
32
+ private
33
+
34
+ #
35
+ # Construct the PostGIS SQL query
36
+ #
37
+ # Returns:
38
+ # Area/Distance/DWithin/Length/Perimeter => projected units
39
+ # DistanceSphere/Spheroid => meters
40
+ #
41
+ def construct_geometric_sql(type,geoms,options)
42
+ not_db, on_db = geoms.partition { |g| g.is_a?(Geometry) || g.new_record? }
43
+ not_db.map! {|o| o.respond_to?(:new_record?) ? o.geom : o }
44
+
45
+ tables = on_db.map do |t| {
46
+ :name => t.class.table_name,
47
+ :column => t.postgis_geoms.keys[0],
48
+ :uid => unique_identifier,
49
+ :id => t[:id] }
50
+ end
51
+
52
+ # Implement a better way for options?
53
+ if options.instance_of? Hash
54
+ transform = options.delete(:transform)
55
+ stcollect = options.delete(:stcollect)
56
+ options = nil
57
+ end
58
+
59
+ fields = tables.map { |f| "#{f[:uid]}.#{f[:column]}" } # W1.geom
60
+ fields << not_db.map { |g| "'#{g.as_hex_ewkb}'::geometry"} unless not_db.empty?
61
+ fields.map! { |f| "ST_Transform(#{f}, #{transform})" } if transform # ST_Transform(W1.geom,x)
62
+ fields.map! { |f| "ST_Union(#{f})" } if stcollect # ST_Transform(W1.geom,x)
63
+ conditions = tables.map {|f| "#{f[:uid]}.id = #{f[:id]}" } # W1.id = 5
64
+ tables.map! { |f| "#{f[:name]} #{f[:uid]}" } # streets W1
65
+
66
+ #
67
+ # Data => SELECT Func(A,B)
68
+ # BBox => SELECT (A <=> B)
69
+ # Func => SELECT Func(Func(A))
70
+ #
71
+ if type != :bbox
72
+ opcode = type.to_s
73
+ opcode = "ST_#{opcode}" unless opcode =~ /th3d|pesinter/
74
+ fields << options if options
75
+ fields = fields.join(",")
76
+ else
77
+ fields = fields.join(" #{options} ")
78
+ end
79
+
80
+
81
+ sql = "SELECT #{opcode}(#{fields}) "
82
+ sql << "FROM #{tables.join(",")} " unless tables.empty?
83
+ sql << "WHERE #{conditions.join(" AND ")}" unless conditions.empty?
84
+ sql
85
+ end
86
+
87
+ #
88
+ # Execute the query and parse the return.
89
+ # We may receive:
90
+ #
91
+ # "t" or "f" for boolean queries
92
+ # BIGHASH for geometries
93
+ # HASH for ST_Relate
94
+ # Rescue a float
95
+ #
96
+ def execute_geometrical_calculation(operation, subject, options) #:nodoc:
97
+ value = connection.select_value(construct_geometric_sql(operation, subject, options))
98
+ return nil unless value
99
+ # TODO: bench case vs if here
100
+ if value =~ /^[tf]$/
101
+ {"f" => false, "t" => true}[value]
102
+ elsif value =~ /^\{/
103
+ value
104
+ else
105
+ GeoRuby::SimpleFeatures::Geometry.from_hex_ewkb(value) rescue value
106
+ end
107
+ rescue Exception => e
108
+ raise StandardError, e.to_s #+ e.backtrace.inspect
109
+ end
110
+
111
+ # Get a unique ID for tables
112
+ def unique_identifier
113
+ @u_id ||= "T1"
114
+ @u_id = @u_id.succ
115
+ end
116
+
117
+ end
118
+ end
119
+ #
120
+ # POINT(0 0)
121
+ # LINESTRING(0 0,1 1,1 2)
122
+ # POLYGON((0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1))
123
+ # MULTIPOINT(0 0,1 2)
124
+ # MULTILINESTRING((0 0,1 1,1 2),(2 3,3 2,5 4))
125
+ # MULTIPOLYGON(((0 0,4 0,4 4,0 4,0 0),(1 1,2 1,2 2,1 2,1 1)), ..)
126
+ # GEOMETRYCOLLECTION(POINT(2 3),LINESTRING((2 3,3 4)))
127
+ #
128
+ #Accessors
129
+ #
130
+ #ST_Dump
131
+ #ST_ExteriorRing
132
+ #ST_GeometryN
133
+ #ST_GeometryType
134
+ #ST_InteriorRingN
135
+ #ST_IsEmpty
136
+ #ST_IsRing
137
+ #ST_IsSimple
138
+ #ST_IsValid
139
+ #ST_mem_size
140
+ #ST_M
141
+ #ST_NumGeometries
142
+ #ST_NumInteriorRings
143
+ #ST_PointN
144
+ #ST_SetSRID
145
+ #ST_Summary1
146
+ #ST_X
147
+ #ST_XMin,ST_XMax
148
+ #ST_Y
149
+ #YMin,YMax
150
+ #ST_Z
151
+ #ZMin,ZMax
152
+
153
+ #OUTPUT
154
+
155
+ #ST_AsBinary
156
+ #ST_AsText
157
+ #ST_AsEWKB
158
+ #ST_AsEWKT
159
+ #ST_AsHEXEWKB
160
+ #ST_AsGML
161
+ #ST_AsKML
162
+ #ST_AsSVG
163
+ # #EARTH_SPHEROID = "'SPHEROID[\"IERS_2003\",6378136.6,298.25642]'" # SRID =>
164
+ # def distance_convert(value, unit, from = nil)
165
+ # factor = case unit
166
+ # when :km, :kilo then 1
167
+ # when :miles,:mile then 0.62137119
168
+ # when :cm, :cent then 0.1
169
+ # when :nmi, :nmile then 0.5399568
170
+ # end
171
+ # factor *= 1e3 if from
172
+ # value * factor
173
+ # end #use all commands in lowcase form
174
+ #opcode = opcode.camelize unless opcode =~ /spher|max|npoints/
@@ -0,0 +1,7 @@
1
+ module PostgisAdapter
2
+ class Railtie < Rails::Railtie
3
+ initializer "postgis adapter" do
4
+ require "postgis_adapter"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,446 @@
1
+ #
2
+ # PostGIS Adapter
3
+ #
4
+ #
5
+ # Code from
6
+ # http://georuby.rubyforge.org Spatial Adapter
7
+ #
8
+ require 'active_record'
9
+ require 'active_record/connection_adapters/postgresql_adapter'
10
+ require 'geo_ruby'
11
+ require 'postgis_adapter/common_spatial_adapter'
12
+ require 'postgis_adapter/functions'
13
+ require 'postgis_adapter/functions/common'
14
+ require 'postgis_adapter/functions/class'
15
+ require 'postgis_adapter/functions/bbox'
16
+ require 'postgis_adapter/acts_as_geom'
17
+
18
+ include GeoRuby::SimpleFeatures
19
+ include SpatialAdapter
20
+
21
+ module PostgisAdapter
22
+ IGNORE_TABLES = %w{ spatial_ref_sys geometry_columns geography_columns }
23
+ end
24
+ #tables to ignore in migration : relative to PostGIS management of geometric columns
25
+ ActiveRecord::SchemaDumper.ignore_tables.concat PostgisAdapter::IGNORE_TABLES
26
+
27
+ #add a method to_yaml to the Geometry class which will transform a geometry in a form suitable to be used in a YAML file (such as in a fixture)
28
+ GeoRuby::SimpleFeatures::Geometry.class_eval do
29
+ def to_fixture_format
30
+ as_hex_ewkb
31
+ end
32
+ end
33
+
34
+ ActiveRecord::Base.class_eval do
35
+
36
+ #Vit Ondruch & Tilmann Singer 's patch
37
+ def self.get_conditions(attrs)
38
+ attrs.map do |attr, value|
39
+ attr = attr.to_s
40
+ column_name = connection.quote_column_name(attr)
41
+ if columns_hash[attr].is_a?(SpatialColumn)
42
+ if value.is_a?(Array)
43
+ attrs[attr.to_sym]= "BOX3D(" + value[0].join(" ") + "," + value[1].join(" ") + ")"
44
+ "#{table_name}.#{column_name} && SetSRID(?::box3d, #{value[2] || @@default_srid || DEFAULT_SRID} ) "
45
+ elsif value.is_a?(Envelope)
46
+ attrs[attr.to_sym]= "BOX3D(" + value.lower_corner.text_representation + "," + value.upper_corner.text_representation + ")"
47
+ "#{table_name}.#{column_name} && SetSRID(?::box3d, #{value.srid} ) "
48
+ else
49
+ "#{table_name}.#{column_name} && ? "
50
+ end
51
+ else
52
+ attribute_condition("#{table_name}.#{column_name}", value)
53
+ end
54
+ end.join(' AND ')
55
+ end
56
+
57
+ #For Rails >= 2
58
+ if method(:sanitize_sql_hash_for_conditions).arity == 1
59
+ # Before Rails 2.3.3, the method had only one argument
60
+ def self.sanitize_sql_hash_for_conditions(attrs)
61
+ conditions = get_conditions(attrs)
62
+ replace_bind_variables(conditions, expand_range_bind_variables(attrs.values))
63
+ end
64
+ elsif method(:sanitize_sql_hash_for_conditions).arity == -2
65
+ # After Rails 2.3.3, the method had only two args, the last one optional
66
+ def self.sanitize_sql_hash_for_conditions(attrs, table_name = quoted_table_name)
67
+ attrs = expand_hash_conditions_for_aggregates(attrs)
68
+
69
+ conditions = attrs.map do |attr, value|
70
+ unless value.is_a?(Hash)
71
+ attr = attr.to_s
72
+
73
+ # Extract table name from qualified attribute names.
74
+ if attr.include?('.')
75
+ table_name, attr = attr.split('.', 2)
76
+ table_name = connection.quote_table_name(table_name)
77
+ end
78
+
79
+ if columns_hash[attr].is_a?(SpatialColumn)
80
+ if value.is_a?(Array)
81
+ attrs[attr.to_sym]= "BOX3D(" + value[0].join(" ") + "," + value[1].join(" ") + ")"
82
+ "#{table_name}.#{connection.quote_column_name(attr)} && SetSRID(?::box3d, #{value[2] || DEFAULT_SRID} ) "
83
+ elsif value.is_a?(Envelope)
84
+ attrs[attr.to_sym]= "BOX3D(" + value.lower_corner.text_representation + "," + value.upper_corner.text_representation + ")"
85
+ "#{table_name}.#{connection.quote_column_name(attr)} && SetSRID(?::box3d, #{value.srid} ) "
86
+ else
87
+ "#{table_name}.#{connection.quote_column_name(attr)} && ? "
88
+ end
89
+ else
90
+ attribute_condition("#{table_name}.#{connection.quote_column_name(attr)}", value)
91
+ end
92
+ else
93
+ sanitize_sql_hash_for_conditions(value, connection.quote_table_name(attr.to_s))
94
+ end
95
+ end.join(' AND ')
96
+
97
+ replace_bind_variables(conditions, expand_range_bind_variables(attrs.values))
98
+ end
99
+ else
100
+ raise "Spatial Adapter will not work with this version of Rails"
101
+ end
102
+ end
103
+
104
+ ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do
105
+
106
+ include SpatialAdapter
107
+
108
+ # SCHEMA STATEMENTS ========================================
109
+ #
110
+ # Use :template on database.yml seems a better practice.
111
+ #
112
+ # alias :original_recreate_database :recreate_database
113
+ # def recreate_database(configuration, enc_option)
114
+ # `dropdb -U "#{configuration["test"]["username"]}" #{configuration["test"]["database"]}`
115
+ # `createdb #{enc_option} -U "#{configuration["test"]["username"]}" #{configuration["test"]["database"]}`
116
+ # `createlang -U "#{configuration["test"]["username"]}" plpgsql #{configuration["test"]["database"]}`
117
+ # `psql -d #{configuration["test"]["database"]} -f db/spatial/postgis.sql`
118
+ # `psql -d #{configuration["test"]["database"]} -f db/spatial/spatial_ref_sys.sql`
119
+ # end
120
+
121
+ # alias :original_create_database :create_database
122
+ # def create_database(name, options = {})
123
+ # original_create_database(name, options = {})
124
+ # `createlang plpgsql #{name}`
125
+ # `psql -d #{name} -f db/spatial/postgis.sql`
126
+ # `psql -d #{name} -f db/spatial/spatial_ref_sys.sql`
127
+ # end
128
+
129
+ alias :original_native_database_types :native_database_types
130
+ def native_database_types
131
+ original_native_database_types.update(geometry_data_types)
132
+ end
133
+
134
+ alias :original_quote :quote
135
+ #Redefines the quote method to add behaviour for when a Geometry is encountered
136
+ def quote(value, column = nil)
137
+ if value.kind_of?(GeoRuby::SimpleFeatures::Geometry)
138
+ "'#{value.as_hex_ewkb}'"
139
+ else
140
+ original_quote(value,column)
141
+ end
142
+ end
143
+
144
+ alias :original_tables :tables
145
+ def tables(name = nil) #:nodoc:
146
+ original_tables(name) + views(name)
147
+ end
148
+
149
+ def views(name = nil) #:nodoc:
150
+ schemas = schema_search_path.split(/,/).map { |p| quote(p.strip) }.join(',')
151
+ query(<<-SQL, name).map { |row| row[0] }
152
+ SELECT viewname
153
+ FROM pg_views
154
+ WHERE schemaname IN (#{schemas})
155
+ SQL
156
+ end
157
+
158
+ def create_table(name, options = {})
159
+ table_definition = ActiveRecord::ConnectionAdapters::PostgreSQLTableDefinition.new(self)
160
+ table_definition.primary_key(options[:primary_key] || "id") unless options[:id] == false
161
+
162
+ yield table_definition
163
+
164
+ if options[:force]
165
+ drop_table(name) rescue nil
166
+ end
167
+
168
+ create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
169
+ create_sql << "#{name} ("
170
+ create_sql << table_definition.to_sql
171
+ create_sql << ") #{options[:options]}"
172
+ execute create_sql
173
+
174
+ #added to create the geometric columns identified during the table definition
175
+ unless table_definition.geom_columns.nil?
176
+ table_definition.geom_columns.each do |geom_column|
177
+ execute geom_column.to_sql(name)
178
+ end
179
+ end
180
+ end
181
+
182
+ alias :original_remove_column :remove_column
183
+ def remove_column(table_name,column_name, options = {})
184
+ columns(table_name).each do |col|
185
+ if col.name == column_name.to_s
186
+ #check if the column is geometric
187
+ unless geometry_data_types[col.type].nil? or
188
+ (options[:remove_using_dropgeometrycolumn] == false)
189
+ execute "SELECT DropGeometryColumn('#{table_name}','#{column_name}')"
190
+ else
191
+ original_remove_column(table_name,column_name)
192
+ end
193
+ end
194
+ end
195
+ end
196
+
197
+ alias :original_add_column :add_column
198
+ def add_column(table_name, column_name, type, options = {})
199
+ unless geometry_data_types[type].nil? or (options[:create_using_addgeometrycolumn] == false)
200
+ geom_column = ActiveRecord::ConnectionAdapters::PostgreSQLColumnDefinition.
201
+ new(self,column_name, type, nil,nil,options[:null],options[:srid] || -1 ,
202
+ options[:with_z] || false , options[:with_m] || false)
203
+
204
+ execute geom_column.to_sql(table_name)
205
+ else
206
+ original_add_column(table_name,column_name,type,options)
207
+ end
208
+ end
209
+
210
+ # Adds a GIST spatial index to a column. Its name will be
211
+ # <table_name>_<column_name>_spatial_index unless
212
+ # the key :name is present in the options hash, in which case its
213
+ # value is taken as the name of the index.
214
+ def add_index(table_name, column_name, options = {})
215
+ index_name = options[:name] || index_name(table_name,:column => Array(column_name))
216
+ if options[:spatial]
217
+ execute "CREATE INDEX #{index_name} ON #{table_name} USING GIST (#{Array(column_name).join(", ")} GIST_GEOMETRY_OPS)"
218
+ else
219
+ super
220
+ end
221
+ end
222
+
223
+ def indexes(table_name, name = nil) #:nodoc:
224
+ result = query(<<-SQL, name)
225
+ SELECT i.relname, d.indisunique, a.attname , am.amname
226
+ FROM pg_class t, pg_class i, pg_index d, pg_attribute a, pg_am am
227
+ WHERE i.relkind = 'i'
228
+ AND d.indexrelid = i.oid
229
+ AND d.indisprimary = 'f'
230
+ AND t.oid = d.indrelid
231
+ AND i.relam = am.oid
232
+ AND t.relname = '#{table_name}'
233
+ AND a.attrelid = t.oid
234
+ AND ( d.indkey[0]=a.attnum OR d.indkey[1]=a.attnum
235
+ OR d.indkey[2]=a.attnum OR d.indkey[3]=a.attnum
236
+ OR d.indkey[4]=a.attnum OR d.indkey[5]=a.attnum
237
+ OR d.indkey[6]=a.attnum OR d.indkey[7]=a.attnum
238
+ OR d.indkey[8]=a.attnum OR d.indkey[9]=a.attnum )
239
+ ORDER BY i.relname
240
+ SQL
241
+
242
+ current_index = nil
243
+ indexes = []
244
+
245
+ result.each do |row|
246
+ if current_index != row[0]
247
+ #index type gist indicates a spatial index (probably not totally true but let's simplify!)
248
+ indexes << ActiveRecord::ConnectionAdapters::IndexDefinition.
249
+ new(table_name, row[0], row[1] == "t", row[3] == "gist" ,[])
250
+
251
+ current_index = row[0]
252
+ end
253
+ indexes.last.columns << row[2]
254
+ end
255
+ indexes
256
+ end
257
+
258
+ def columns(table_name, name = nil) #:nodoc:
259
+ raw_geom_infos = column_spatial_info(table_name)
260
+
261
+ column_definitions(table_name).collect do |name, type, default, notnull|
262
+ if type =~ /geometry/i
263
+ raw_geom_info = raw_geom_infos[name]
264
+ if raw_geom_info.nil?
265
+ ActiveRecord::ConnectionAdapters::SpatialPostgreSQLColumn.create_simplified(name, default, notnull == "f")
266
+ else
267
+ 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)
268
+ end
269
+ else
270
+ ActiveRecord::ConnectionAdapters::PostgreSQLColumn.new(name, default, type, notnull == "f")
271
+ end
272
+ end
273
+ end
274
+
275
+ # For version of Rails where exists disable_referential_integrity
276
+ if self.instance_methods.include? :disable_referential_integrity
277
+ #Pete Deffendol's patch
278
+ alias :original_disable_referential_integrity :disable_referential_integrity
279
+ def disable_referential_integrity(&block) #:nodoc:
280
+ execute(tables.reject { |name| PostgisAdapter::IGNORE_TABLES.include?(name) }.map { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
281
+ yield
282
+ ensure
283
+ execute(tables.reject { |name| PostgisAdapter::IGNORE_TABLES.include?(name) }.map { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";"))
284
+ end
285
+ end
286
+
287
+ private
288
+
289
+ def column_spatial_info(table_name)
290
+ constr = query <<-end_sql
291
+ SELECT * FROM geometry_columns WHERE f_table_name = '#{table_name}'
292
+ end_sql
293
+
294
+ raw_geom_infos = {}
295
+ constr.each do |constr_def_a|
296
+ raw_geom_infos[constr_def_a[3]] ||= ActiveRecord::ConnectionAdapters::RawGeomInfo.new
297
+ raw_geom_infos[constr_def_a[3]].type = constr_def_a[6]
298
+ raw_geom_infos[constr_def_a[3]].dimension = constr_def_a[4].to_i
299
+ raw_geom_infos[constr_def_a[3]].srid = constr_def_a[5].to_i
300
+
301
+ if raw_geom_infos[constr_def_a[3]].type[-1] == ?M
302
+ raw_geom_infos[constr_def_a[3]].with_m = true
303
+ raw_geom_infos[constr_def_a[3]].type.chop!
304
+ else
305
+ raw_geom_infos[constr_def_a[3]].with_m = false
306
+ end
307
+ end
308
+
309
+ raw_geom_infos.each_value do |raw_geom_info|
310
+ #check the presence of z and m
311
+ raw_geom_info.convert!
312
+ end
313
+
314
+ raw_geom_infos
315
+ rescue => e
316
+ nil
317
+ end
318
+
319
+ end
320
+
321
+ module ActiveRecord
322
+ module ConnectionAdapters
323
+ class RawGeomInfo < Struct.new(:type,:srid,:dimension,:with_z,:with_m) #:nodoc:
324
+ def convert!
325
+ self.type = "geometry" if self.type.nil? #if geometry the geometrytype constraint is not present : need to set the type here then
326
+
327
+ if dimension == 4
328
+ self.with_m = true
329
+ self.with_z = true
330
+ elsif dimension == 3
331
+ if with_m
332
+ self.with_z = false
333
+ self.with_m = true
334
+ else
335
+ self.with_z = true
336
+ self.with_m = false
337
+ end
338
+ else
339
+ self.with_z = false
340
+ self.with_m = false
341
+ end
342
+ end
343
+ end
344
+ end
345
+ end
346
+
347
+
348
+ module ActiveRecord
349
+ module ConnectionAdapters
350
+ class PostgreSQLTableDefinition < TableDefinition
351
+ attr_reader :geom_columns
352
+
353
+ def column(name, type, options = {})
354
+ unless (@base.geometry_data_types[type.to_sym].nil? or
355
+ (options[:create_using_addgeometrycolumn] == false))
356
+
357
+ geom_column = PostgreSQLColumnDefinition.new(@base,name, type)
358
+ geom_column.null = options[:null]
359
+ geom_column.srid = options[:srid] || -1
360
+ geom_column.with_z = options[:with_z] || false
361
+ geom_column.with_m = options[:with_m] || false
362
+
363
+ @geom_columns = [] if @geom_columns.nil?
364
+ @geom_columns << geom_column
365
+ else
366
+ super(name,type,options)
367
+ end
368
+ end
369
+
370
+ SpatialAdapter.geometry_data_types.keys.each do |column_type|
371
+ class_eval <<-EOV
372
+ def #{column_type}(*args)
373
+ options = args.extract_options!
374
+ column_names = args
375
+
376
+ column_names.each { |name| column(name, '#{column_type}', options) }
377
+ end
378
+ EOV
379
+ end
380
+ end
381
+
382
+ class PostgreSQLColumnDefinition < ColumnDefinition
383
+ attr_accessor :srid, :with_z,:with_m
384
+ attr_reader :spatial
385
+
386
+ def initialize(base = nil, name = nil, type=nil, limit=nil, default=nil,null=nil,srid=-1,with_z=false,with_m=false)
387
+ super(base, name, type, limit, default,null)
388
+ @spatial=true
389
+ @srid=srid
390
+ @with_z=with_z
391
+ @with_m=with_m
392
+ end
393
+
394
+ def to_sql(table_name)
395
+ if @spatial
396
+ type_sql = type_to_sql(type.to_sym)
397
+ type_sql += "M" if with_m and !with_z
398
+ if with_m and with_z
399
+ dimension = 4
400
+ elsif with_m or with_z
401
+ dimension = 3
402
+ else
403
+ dimension = 2
404
+ end
405
+
406
+ column_sql = "SELECT AddGeometryColumn('#{table_name}','#{name}',#{srid},'#{type_sql}',#{dimension})"
407
+ column_sql += ";ALTER TABLE #{table_name} ALTER #{name} SET NOT NULL" if null == false
408
+ column_sql
409
+ else
410
+ super
411
+ end
412
+ end
413
+
414
+
415
+ private
416
+ def type_to_sql(name, limit=nil)
417
+ base.type_to_sql(name, limit) rescue name
418
+ end
419
+
420
+ end
421
+
422
+ end
423
+ end
424
+
425
+ # Would prefer creation of a PostgreSQLColumn type instead but I would
426
+ # need to reimplement methods where Column objects are instantiated so
427
+ # I leave it like this
428
+ module ActiveRecord
429
+ module ConnectionAdapters
430
+ class SpatialPostgreSQLColumn < Column
431
+
432
+ include SpatialColumn
433
+
434
+ #Transforms a string to a geometry. PostGIS returns a HexEWKB string.
435
+ def self.string_to_geometry(string)
436
+ return string unless string.is_a?(String)
437
+ GeoRuby::SimpleFeatures::Geometry.from_hex_ewkb(string) rescue nil
438
+ end
439
+
440
+ def self.create_simplified(name,default,null = true)
441
+ new(name,default,"geometry",null,nil,nil,nil)
442
+ end
443
+
444
+ end
445
+ end
446
+ end
@@ -0,0 +1,19 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = 'gotime-postgis_adapter'
3
+ spec.version = '0.8.2'
4
+ spec.authors = ['Marcos Piccinini']
5
+ spec.summary = 'PostGIS Adapter for Active Record'
6
+ spec.email = 'x@nofxx.com'
7
+ spec.homepage = 'http://github.com/nofxx/postgis_adapter'
8
+
9
+ spec.rdoc_options = ['--charset=UTF-8']
10
+ spec.rubyforge_project = 'postgis_adapter'
11
+
12
+ spec.files = Dir['**/*'].reject{ |f| f.include?('git') }
13
+ spec.test_files = Dir['spec/**/*.rb']
14
+ spec.extra_rdoc_files = ['README.rdoc']
15
+
16
+ spec.add_dependency 'nofxx-georuby'
17
+
18
+ spec.description = 'Execute PostGIS functions on Active Record'
19
+ end
data/rails/init.rb ADDED
@@ -0,0 +1,28 @@
1
+ # Patch Arel to support geometry type.
2
+ module Arel
3
+ module Attributes
4
+ class << self
5
+ alias original_for for
6
+
7
+ def for(column)
8
+ case column.type
9
+ when :geometry then String
10
+ else
11
+ original_for(column)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ class SpatialAdapterNotCompatibleError < StandardError
19
+ end
20
+
21
+ unless ActiveRecord::Base.connection.adapter_name.downcase == 'postgresql'
22
+ error_message = "Database config file not set or it does not map to "
23
+ error_message << "PostgreSQL.\nOnly PostgreSQL with PostGIS is supported "
24
+ error_message << "by postgis_adapter.")
25
+ raise SpatialAdapterNotCompatibleError.new(error_message)
26
+ end
27
+
28
+ require 'postgis_adapter'