gotime-postgis_adapter 0.8.2

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,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'