postgis_adapter 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +6 -0
- data/MIT-LICENSE +21 -0
- data/Manifest.txt +36 -0
- data/README.rdoc +311 -0
- data/Rakefile +100 -0
- data/init.rb +1 -0
- data/install.rb +0 -0
- data/lib/postgis_adapter.rb +388 -0
- data/lib/postgis_adapter/acts_as_geom.rb +39 -0
- data/lib/postgis_adapter/common_spatial_adapter.rb +179 -0
- data/lib/postgis_functions.rb +158 -0
- data/lib/postgis_functions/bbox.rb +128 -0
- data/lib/postgis_functions/class.rb +64 -0
- data/lib/postgis_functions/common.rb +438 -0
- data/lib/postgis_functions/linestring.rb +172 -0
- data/lib/postgis_functions/point.rb +89 -0
- data/lib/postgis_functions/polygon.rb +78 -0
- data/postgis_adapter.gemspec +38 -0
- data/rails/init.rb +8 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/spec/acts_as_geom_spec.rb +15 -0
- data/spec/common_spatial_adapter_spec.rb +254 -0
- data/spec/db/database_postgis.yml +4 -0
- data/spec/db/models_postgis.rb +56 -0
- data/spec/db/schema_postgis.rb +86 -0
- data/spec/postgis_adapter_spec.rb +174 -0
- data/spec/postgis_functions/bbox_spec.rb +84 -0
- data/spec/postgis_functions/linestring_spec.rb +219 -0
- data/spec/postgis_functions/point_spec.rb +136 -0
- data/spec/postgis_functions/polygon_spec.rb +146 -0
- data/spec/postgis_functions_spec.rb +51 -0
- data/spec/spec.opts +5 -0
- data/spec/spec_helper.rb +25 -0
- data/uninstall.rb +0 -0
- metadata +121 -0
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/rails/init"
|
data/install.rb
ADDED
File without changes
|
@@ -0,0 +1,388 @@
|
|
1
|
+
# #
|
2
|
+
# PostGIS Adapter
|
3
|
+
#
|
4
|
+
# Spatial Adapter PostGIS Adapter for ActiveRecord
|
5
|
+
#
|
6
|
+
#
|
7
|
+
#require 'active_record'
|
8
|
+
require 'geo_ruby'
|
9
|
+
require 'postgis_adapter/common_spatial_adapter'
|
10
|
+
require 'postgis_functions'
|
11
|
+
require 'postgis_functions/common'
|
12
|
+
require 'postgis_functions/class'
|
13
|
+
require 'postgis_functions/point'
|
14
|
+
require 'postgis_functions/linestring'
|
15
|
+
require 'postgis_functions/polygon'
|
16
|
+
require 'postgis_functions/bbox'
|
17
|
+
require 'postgis_adapter/acts_as_geom'
|
18
|
+
|
19
|
+
include GeoRuby::SimpleFeatures
|
20
|
+
include SpatialAdapter
|
21
|
+
|
22
|
+
module PostgisAdapter
|
23
|
+
VERSION = '0.1.8'
|
24
|
+
end
|
25
|
+
|
26
|
+
#tables to ignore in migration : relative to PostGIS management of geometric columns
|
27
|
+
ActiveRecord::SchemaDumper.ignore_tables << "spatial_ref_sys" << "geometry_columns"
|
28
|
+
|
29
|
+
|
30
|
+
#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)
|
31
|
+
GeoRuby::SimpleFeatures::Geometry.class_eval do
|
32
|
+
def to_fixture_format
|
33
|
+
as_hex_ewkb
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
ActiveRecord::Base.class_eval do
|
39
|
+
require 'active_record/version'
|
40
|
+
|
41
|
+
#Vit Ondruch & Tilmann Singer 's patch
|
42
|
+
def self.get_conditions(attrs)
|
43
|
+
attrs.map do |attr, value|
|
44
|
+
attr = attr.to_s
|
45
|
+
if columns_hash[attr].is_a?(SpatialColumn)
|
46
|
+
if value.is_a?(Array)
|
47
|
+
attrs[attr.to_sym]= "BOX3D(" + value[0].join(" ") + "," + value[1].join(" ") + ")"
|
48
|
+
"#{table_name}.#{connection.quote_column_name(attr)} && SetSRID(?::box3d, #{value[2] || DEFAULT_SRID} ) "
|
49
|
+
elsif value.is_a?(Envelope)
|
50
|
+
attrs[attr.to_sym]= "BOX3D(" + value.lower_corner.text_representation + "," + value.upper_corner.text_representation + ")"
|
51
|
+
"#{table_name}.#{connection.quote_column_name(attr)} && SetSRID(?::box3d, #{value.srid} ) "
|
52
|
+
else
|
53
|
+
"#{table_name}.#{connection.quote_column_name(attr)} && ? "
|
54
|
+
end
|
55
|
+
else
|
56
|
+
"#{table_name}.#{connection.quote_column_name(attr)} #{attribute_condition(value)}"
|
57
|
+
end
|
58
|
+
end.join(' AND ')
|
59
|
+
end
|
60
|
+
|
61
|
+
#For Rails >= 2
|
62
|
+
def self.sanitize_sql_hash_for_conditions(attrs)
|
63
|
+
conditions = get_conditions(attrs)
|
64
|
+
replace_bind_variables(conditions, expand_range_bind_variables(attrs.values))
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do
|
70
|
+
|
71
|
+
include SpatialAdapter
|
72
|
+
|
73
|
+
alias :original_native_database_types :native_database_types
|
74
|
+
def native_database_types
|
75
|
+
original_native_database_types.update(geometry_data_types)
|
76
|
+
end
|
77
|
+
|
78
|
+
alias :original_quote :quote
|
79
|
+
#Redefines the quote method to add behaviour for when a Geometry is encountered
|
80
|
+
def quote(value, column = nil)
|
81
|
+
if value.kind_of?(GeoRuby::SimpleFeatures::Geometry)
|
82
|
+
"'#{value.as_hex_ewkb}'"
|
83
|
+
else
|
84
|
+
original_quote(value,column)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
alias :original_tables :tables
|
89
|
+
def tables(name = nil) #:nodoc:
|
90
|
+
schemas = schema_search_path.split(/,/).map { |p| quote(p.strip) }.join(',')
|
91
|
+
original_tables(name) + query(<<-SQL, name).map { |row| row[0] }
|
92
|
+
SELECT viewname
|
93
|
+
FROM pg_views
|
94
|
+
WHERE schemaname IN (#{schemas})
|
95
|
+
SQL
|
96
|
+
end
|
97
|
+
|
98
|
+
def create_table(name, options = {})
|
99
|
+
table_definition = ActiveRecord::ConnectionAdapters::PostgreSQLTableDefinition.new(self)
|
100
|
+
table_definition.primary_key(options[:primary_key] || "id") unless options[:id] == false
|
101
|
+
|
102
|
+
yield table_definition
|
103
|
+
|
104
|
+
if options[:force]
|
105
|
+
drop_table(name) rescue nil
|
106
|
+
end
|
107
|
+
|
108
|
+
create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
|
109
|
+
create_sql << "#{name} ("
|
110
|
+
create_sql << table_definition.to_sql
|
111
|
+
create_sql << ") #{options[:options]}"
|
112
|
+
execute create_sql
|
113
|
+
|
114
|
+
#added to create the geometric columns identified during the table definition
|
115
|
+
unless table_definition.geom_columns.nil?
|
116
|
+
table_definition.geom_columns.each do |geom_column|
|
117
|
+
execute geom_column.to_sql(name)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
alias :original_remove_column :remove_column
|
123
|
+
def remove_column(table_name,column_name, options = {})
|
124
|
+
columns(table_name).each do |col|
|
125
|
+
if col.name == column_name.to_s
|
126
|
+
#check if the column is geometric
|
127
|
+
unless geometry_data_types[col.type].nil? or
|
128
|
+
(options[:remove_using_dropgeometrycolumn] == false)
|
129
|
+
execute "SELECT DropGeometryColumn('#{table_name}','#{column_name}')"
|
130
|
+
else
|
131
|
+
original_remove_column(table_name,column_name)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
alias :original_add_column :add_column
|
138
|
+
def add_column(table_name, column_name, type, options = {})
|
139
|
+
unless geometry_data_types[type].nil? or (options[:create_using_addgeometrycolumn] == false)
|
140
|
+
geom_column = ActiveRecord::ConnectionAdapters::PostgreSQLColumnDefinition.
|
141
|
+
new(self,column_name, type, nil,nil,options[:null],options[:srid] || -1 ,
|
142
|
+
options[:with_z] || false , options[:with_m] || false)
|
143
|
+
|
144
|
+
execute geom_column.to_sql(table_name)
|
145
|
+
else
|
146
|
+
original_add_column(table_name,column_name,type,options)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# Adds a GIST spatial index to a column. Its name will be
|
151
|
+
# <table_name>_<column_name>_spatial_index unless
|
152
|
+
# the key :name is present in the options hash, in which case its
|
153
|
+
# value is taken as the name of the index.
|
154
|
+
def add_index(table_name,column_name,options = {})
|
155
|
+
index_name = options[:name] || index_name(table_name,:column => Array(column_name))
|
156
|
+
if options[:spatial]
|
157
|
+
execute "CREATE INDEX #{index_name} ON #{table_name} USING GIST (#{Array(column_name).join(", ")} GIST_GEOMETRY_OPS)"
|
158
|
+
else
|
159
|
+
index_type = options[:unique] ? "UNIQUE" : ""
|
160
|
+
#all together
|
161
|
+
execute "CREATE #{index_type} INDEX #{index_name} ON #{table_name} (#{Array(column_name).join(", ")})"
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def indexes(table_name, name = nil) #:nodoc:
|
166
|
+
result = query(<<-SQL, name)
|
167
|
+
SELECT i.relname, d.indisunique, a.attname , am.amname
|
168
|
+
FROM pg_class t, pg_class i, pg_index d, pg_attribute a, pg_am am
|
169
|
+
WHERE i.relkind = 'i'
|
170
|
+
AND d.indexrelid = i.oid
|
171
|
+
AND d.indisprimary = 'f'
|
172
|
+
AND t.oid = d.indrelid
|
173
|
+
AND i.relam = am.oid
|
174
|
+
AND t.relname = '#{table_name}'
|
175
|
+
AND a.attrelid = t.oid
|
176
|
+
AND ( d.indkey[0]=a.attnum OR d.indkey[1]=a.attnum
|
177
|
+
OR d.indkey[2]=a.attnum OR d.indkey[3]=a.attnum
|
178
|
+
OR d.indkey[4]=a.attnum OR d.indkey[5]=a.attnum
|
179
|
+
OR d.indkey[6]=a.attnum OR d.indkey[7]=a.attnum
|
180
|
+
OR d.indkey[8]=a.attnum OR d.indkey[9]=a.attnum )
|
181
|
+
ORDER BY i.relname
|
182
|
+
SQL
|
183
|
+
|
184
|
+
current_index = nil
|
185
|
+
indexes = []
|
186
|
+
|
187
|
+
result.each do |row|
|
188
|
+
if current_index != row[0]
|
189
|
+
#index type gist indicates a spatial index (probably not totally true but let's simplify!)
|
190
|
+
indexes << ActiveRecord::ConnectionAdapters::IndexDefinition.
|
191
|
+
new(table_name, row[0], row[1] == "t", row[3] == "gist" ,[])
|
192
|
+
|
193
|
+
current_index = row[0]
|
194
|
+
end
|
195
|
+
indexes.last.columns << row[2]
|
196
|
+
end
|
197
|
+
indexes
|
198
|
+
end
|
199
|
+
|
200
|
+
def columns(table_name, name = nil) #:nodoc:
|
201
|
+
raw_geom_infos = column_spatial_info(table_name)
|
202
|
+
|
203
|
+
column_definitions(table_name).collect do |name, type, default, notnull|
|
204
|
+
if type =~ /geometry/i
|
205
|
+
raw_geom_info = raw_geom_infos[name]
|
206
|
+
if raw_geom_info.nil?
|
207
|
+
ActiveRecord::ConnectionAdapters::SpatialPostgreSQLColumn.create_simplified(name,default,notnull == "f")
|
208
|
+
else
|
209
|
+
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)
|
210
|
+
end
|
211
|
+
else
|
212
|
+
ActiveRecord::ConnectionAdapters::Column.new(name, ActiveRecord::ConnectionAdapters::PostgreSQLColumn.extract_value_from_default( default), type,notnull == "f")
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
# # For version of Rails where exists disable_referential_integrity
|
218
|
+
# if self.instance_methods.include? "disable_referential_integrity"
|
219
|
+
# #Pete Deffendol's patch
|
220
|
+
# alias :original_disable_referential_integrity :disable_referential_integrity
|
221
|
+
# def disable_referential_integrity(&block) #:nodoc:
|
222
|
+
# ignore_tables = %w{ geometry_columns spatial_ref_sys }
|
223
|
+
# execute(tables.select { |name| !ignore_tables.include?(name) }.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
|
224
|
+
# yield
|
225
|
+
# ensure
|
226
|
+
# execute(tables.select { |name| !ignore_tables.include?(name)}.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";"))
|
227
|
+
# end
|
228
|
+
# end
|
229
|
+
|
230
|
+
private
|
231
|
+
|
232
|
+
def column_spatial_info(table_name)
|
233
|
+
constr = query <<-end_sql
|
234
|
+
SELECT * FROM geometry_columns WHERE f_table_name = '#{table_name}'
|
235
|
+
end_sql
|
236
|
+
|
237
|
+
raw_geom_infos = {}
|
238
|
+
constr.each do |constr_def_a|
|
239
|
+
raw_geom_infos[constr_def_a[3]] ||= ActiveRecord::ConnectionAdapters::RawGeomInfo.new
|
240
|
+
raw_geom_infos[constr_def_a[3]].type = constr_def_a[6]
|
241
|
+
raw_geom_infos[constr_def_a[3]].dimension = constr_def_a[4].to_i
|
242
|
+
raw_geom_infos[constr_def_a[3]].srid = constr_def_a[5].to_i
|
243
|
+
|
244
|
+
if raw_geom_infos[constr_def_a[3]].type[-1] == ?M
|
245
|
+
raw_geom_infos[constr_def_a[3]].with_m = true
|
246
|
+
raw_geom_infos[constr_def_a[3]].type.chop!
|
247
|
+
else
|
248
|
+
raw_geom_infos[constr_def_a[3]].with_m = false
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
raw_geom_infos.each_value do |raw_geom_info|
|
253
|
+
#check the presence of z and m
|
254
|
+
raw_geom_info.convert!
|
255
|
+
end
|
256
|
+
|
257
|
+
raw_geom_infos
|
258
|
+
|
259
|
+
end
|
260
|
+
|
261
|
+
end
|
262
|
+
|
263
|
+
module ActiveRecord
|
264
|
+
module ConnectionAdapters
|
265
|
+
class RawGeomInfo < Struct.new(:type,:srid,:dimension,:with_z,:with_m) #:nodoc:
|
266
|
+
def convert!
|
267
|
+
self.type = "geometry" if self.type.nil? #if geometry the geometrytype constraint is not present : need to set the type here then
|
268
|
+
|
269
|
+
if dimension == 4
|
270
|
+
self.with_m = true
|
271
|
+
self.with_z = true
|
272
|
+
elsif dimension == 3
|
273
|
+
if with_m
|
274
|
+
self.with_z = false
|
275
|
+
self.with_m = true
|
276
|
+
else
|
277
|
+
self.with_z = true
|
278
|
+
self.with_m = false
|
279
|
+
end
|
280
|
+
else
|
281
|
+
self.with_z = false
|
282
|
+
self.with_m = false
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
|
290
|
+
module ActiveRecord
|
291
|
+
module ConnectionAdapters
|
292
|
+
class PostgreSQLTableDefinition < TableDefinition
|
293
|
+
attr_reader :geom_columns
|
294
|
+
|
295
|
+
def column(name, type, options = {})
|
296
|
+
unless (@base.geometry_data_types[type.to_sym].nil? or
|
297
|
+
(options[:create_using_addgeometrycolumn] == false))
|
298
|
+
|
299
|
+
geom_column = PostgreSQLColumnDefinition.new(@base,name, type)
|
300
|
+
geom_column.null = options[:null]
|
301
|
+
geom_column.srid = options[:srid] || -1
|
302
|
+
geom_column.with_z = options[:with_z] || false
|
303
|
+
geom_column.with_m = options[:with_m] || false
|
304
|
+
|
305
|
+
@geom_columns = [] if @geom_columns.nil?
|
306
|
+
@geom_columns << geom_column
|
307
|
+
else
|
308
|
+
super(name,type,options)
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
SpatialAdapter.geometry_data_types.keys.each do |column_type|
|
313
|
+
class_eval <<-EOV
|
314
|
+
def #{column_type}(*args)
|
315
|
+
options = args.extract_options!
|
316
|
+
column_names = args
|
317
|
+
|
318
|
+
column_names.each { |name| column(name, '#{column_type}', options) }
|
319
|
+
end
|
320
|
+
EOV
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
class PostgreSQLColumnDefinition < ColumnDefinition
|
325
|
+
attr_accessor :srid, :with_z,:with_m
|
326
|
+
attr_reader :spatial
|
327
|
+
|
328
|
+
def initialize(base = nil, name = nil, type=nil, limit=nil, default=nil,null=nil,srid=-1,with_z=false,with_m=false)
|
329
|
+
super(base, name, type, limit, default,null)
|
330
|
+
@spatial=true
|
331
|
+
@srid=srid
|
332
|
+
@with_z=with_z
|
333
|
+
@with_m=with_m
|
334
|
+
end
|
335
|
+
|
336
|
+
def to_sql(table_name)
|
337
|
+
if @spatial
|
338
|
+
type_sql = type_to_sql(type.to_sym)
|
339
|
+
type_sql += "M" if with_m and !with_z
|
340
|
+
if with_m and with_z
|
341
|
+
dimension = 4
|
342
|
+
elsif with_m or with_z
|
343
|
+
dimension = 3
|
344
|
+
else
|
345
|
+
dimension = 2
|
346
|
+
end
|
347
|
+
|
348
|
+
column_sql = "SELECT AddGeometryColumn('#{table_name}','#{name}',#{srid},'#{type_sql}',#{dimension})"
|
349
|
+
column_sql += ";ALTER TABLE #{table_name} ALTER #{name} SET NOT NULL" if null == false
|
350
|
+
column_sql
|
351
|
+
else
|
352
|
+
super
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
|
357
|
+
private
|
358
|
+
def type_to_sql(name, limit=nil)
|
359
|
+
base.type_to_sql(name, limit) rescue name
|
360
|
+
end
|
361
|
+
|
362
|
+
end
|
363
|
+
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
#Would prefer creation of a PostgreSQLColumn type instead but I would
|
368
|
+
# need to reimplement methods where Column objects are instantiated so
|
369
|
+
# I leave it like this
|
370
|
+
module ActiveRecord
|
371
|
+
module ConnectionAdapters
|
372
|
+
class SpatialPostgreSQLColumn < Column
|
373
|
+
|
374
|
+
include SpatialColumn
|
375
|
+
|
376
|
+
#Transforms a string to a geometry. PostGIS returns a HewEWKB string.
|
377
|
+
def self.string_to_geometry(string)
|
378
|
+
return string unless string.is_a?(String)
|
379
|
+
GeoRuby::SimpleFeatures::Geometry.from_hex_ewkb(string) rescue nil
|
380
|
+
end
|
381
|
+
|
382
|
+
def self.create_simplified(name,default,null = true)
|
383
|
+
new(name,default,"geometry",null,nil,nil,nil)
|
384
|
+
end
|
385
|
+
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# #
|
2
|
+
# PostGIS Adapter
|
3
|
+
#
|
4
|
+
#
|
5
|
+
# http://github.com/nofxx/postgis_adapter
|
6
|
+
#
|
7
|
+
module PostgisFunctions
|
8
|
+
def self.included(base)
|
9
|
+
base.send :extend, ClassMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
|
14
|
+
# acts_as_geom :geom
|
15
|
+
def acts_as_geom(*columns)
|
16
|
+
cattr_accessor :postgis_geoms
|
17
|
+
|
18
|
+
geoms = columns.map do |g|
|
19
|
+
geom_type = get_geom_type(g)
|
20
|
+
case geom_type
|
21
|
+
when :point
|
22
|
+
send :include, PointFunctions
|
23
|
+
when :polygon
|
24
|
+
send :include, PolygonFunctions
|
25
|
+
when :line_string
|
26
|
+
send :include, LineStringFunctions
|
27
|
+
end
|
28
|
+
{g => geom_type}
|
29
|
+
end
|
30
|
+
self.postgis_geoms = {:geoms => geoms}#, :opts => options}
|
31
|
+
end
|
32
|
+
|
33
|
+
def get_geom_type(column)
|
34
|
+
self.columns.select { |c| c.name == column.to_s}.first.geometry_type
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
ActiveRecord::Base.send :include, PostgisFunctions
|
@@ -0,0 +1,179 @@
|
|
1
|
+
# #
|
2
|
+
# PostGIS Adapter
|
3
|
+
#
|
4
|
+
# Common Spatial Adapter for ActiveRecord
|
5
|
+
#
|
6
|
+
#
|
7
|
+
#Addition of a flag indicating if the index is spatial
|
8
|
+
ActiveRecord::ConnectionAdapters::IndexDefinition.class_eval do
|
9
|
+
attr_accessor :spatial
|
10
|
+
|
11
|
+
def initialize(table, name, unique, spatial,columns)
|
12
|
+
super(table,name,unique,columns)
|
13
|
+
@spatial = spatial
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
ActiveRecord::SchemaDumper.class_eval do
|
19
|
+
def table(table, stream)
|
20
|
+
|
21
|
+
columns = @connection.columns(table)
|
22
|
+
begin
|
23
|
+
tbl = StringIO.new
|
24
|
+
|
25
|
+
if @connection.respond_to?(:pk_and_sequence_for)
|
26
|
+
pk, pk_seq = @connection.pk_and_sequence_for(table)
|
27
|
+
end
|
28
|
+
pk ||= 'id'
|
29
|
+
|
30
|
+
tbl.print " create_table #{table.inspect}"
|
31
|
+
if columns.detect { |c| c.name == pk }
|
32
|
+
if pk != 'id'
|
33
|
+
tbl.print %Q(, :primary_key => "#{pk}")
|
34
|
+
end
|
35
|
+
else
|
36
|
+
tbl.print ", :id => false"
|
37
|
+
end
|
38
|
+
|
39
|
+
if @connection.respond_to?(:options_for)
|
40
|
+
res = @connection.options_for(table)
|
41
|
+
tbl.print ", :options=>'#{res}'" if res
|
42
|
+
end
|
43
|
+
|
44
|
+
tbl.print ", :force => true"
|
45
|
+
tbl.puts " do |t|"
|
46
|
+
|
47
|
+
columns.each do |column|
|
48
|
+
|
49
|
+
raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" if @types[column.type].nil?
|
50
|
+
next if column.name == pk
|
51
|
+
#need to use less_simplified_type here or have each specific geometry type be simplified to a specific simplified type in Column and each one treated separately in the Column methods
|
52
|
+
if column.is_a?(SpatialColumn)
|
53
|
+
tbl.print " t.column #{column.name.inspect}, #{column.geometry_type.inspect}"
|
54
|
+
tbl.print ", :srid => #{column.srid.inspect}" if column.srid != -1
|
55
|
+
tbl.print ", :with_z => #{column.with_z.inspect}" if column.with_z
|
56
|
+
tbl.print ", :with_m => #{column.with_m.inspect}" if column.with_m
|
57
|
+
else
|
58
|
+
tbl.print " t.column #{column.name.inspect}, #{column.type.inspect}"
|
59
|
+
end
|
60
|
+
tbl.print ", :limit => #{column.limit.inspect}" if column.limit != @types[column.type][:limit]
|
61
|
+
tbl.print ", :default => #{column.default.inspect}" if !column.default.nil?
|
62
|
+
tbl.print ", :null => false" if !column.null
|
63
|
+
tbl.puts
|
64
|
+
end
|
65
|
+
|
66
|
+
tbl.puts " end"
|
67
|
+
tbl.puts
|
68
|
+
indexes(table, tbl)
|
69
|
+
tbl.rewind
|
70
|
+
stream.print tbl.read
|
71
|
+
rescue => e
|
72
|
+
stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
|
73
|
+
stream.puts "# #{e.message} #{e.backtrace}"
|
74
|
+
stream.puts
|
75
|
+
end
|
76
|
+
|
77
|
+
stream end
|
78
|
+
|
79
|
+
def indexes(table, stream)
|
80
|
+
indexes = @connection.indexes(table)
|
81
|
+
indexes.each do |index|
|
82
|
+
stream.print " add_index #{index.table.inspect}, #{index.columns.inspect}, :name => #{index.name.inspect}"
|
83
|
+
stream.print ", :unique => true" if index.unique
|
84
|
+
stream.print ", :spatial=> true " if index.spatial
|
85
|
+
stream.puts
|
86
|
+
end
|
87
|
+
|
88
|
+
stream.puts unless indexes.empty?
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
|
94
|
+
|
95
|
+
module SpatialAdapter
|
96
|
+
#Translation of geometric data types
|
97
|
+
def geometry_data_types
|
98
|
+
{
|
99
|
+
:point => { :name => "POINT" },
|
100
|
+
:line_string => { :name => "LINESTRING" },
|
101
|
+
:polygon => { :name => "POLYGON" },
|
102
|
+
:geometry_collection => { :name => "GEOMETRYCOLLECTION" },
|
103
|
+
:multi_point => { :name => "MULTIPOINT" },
|
104
|
+
:multi_line_string => { :name => "MULTILINESTRING" },
|
105
|
+
:multi_polygon => { :name => "MULTIPOLYGON" },
|
106
|
+
:geometry => { :name => "GEOMETRY"}
|
107
|
+
}
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
#using a mixin instead of subclassing Column since each adapter can have a specific subclass of Column
|
114
|
+
module SpatialColumn
|
115
|
+
attr_reader :geometry_type, :srid, :with_z, :with_m
|
116
|
+
|
117
|
+
def initialize(name, default, sql_type = nil, null = true,srid=-1,with_z=false,with_m=false)
|
118
|
+
super(name,default,sql_type,null)
|
119
|
+
@geometry_type = geometry_simplified_type(@sql_type)
|
120
|
+
@srid = srid
|
121
|
+
@with_z = with_z
|
122
|
+
@with_m = with_m
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
#Redefines type_cast to add support for geometries
|
127
|
+
def type_cast(value)
|
128
|
+
return nil if value.nil?
|
129
|
+
case type
|
130
|
+
when :geometry then self.class.string_to_geometry(value)
|
131
|
+
else super
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
#Redefines type_cast_code to add support for geometries.
|
136
|
+
#
|
137
|
+
#WARNING : Since ActiveRecord keeps only the string values directly returned from the database, it translates from these to the correct types everytime an attribute is read (using the code returned by this method), which is probably ok for simple types, but might be less than efficient for geometries. Also you cannot modify the geometry object returned directly or your change will not be saved.
|
138
|
+
def type_cast_code(var_name)
|
139
|
+
case type
|
140
|
+
when :geometry then "#{self.class.name}.string_to_geometry(#{var_name})"
|
141
|
+
else super
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
#Redefines klass to add support for geometries
|
147
|
+
def klass
|
148
|
+
case type
|
149
|
+
when :geometry then GeoRuby::SimpleFeatures::Geometry
|
150
|
+
else super
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
|
156
|
+
#Redefines the simplified_type method to add behaviour for when a column is of type geometry
|
157
|
+
def simplified_type(field_type)
|
158
|
+
case field_type
|
159
|
+
when /geometry|point|linestring|polygon|multipoint|multilinestring|multipolygon|geometrycollection/i then :geometry
|
160
|
+
else super
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
#less simlpified geometric type to be use in migrations
|
165
|
+
def geometry_simplified_type(field_type)
|
166
|
+
case field_type
|
167
|
+
when /^point$/i then :point
|
168
|
+
when /^linestring$/i then :line_string
|
169
|
+
when /^polygon$/i then :polygon
|
170
|
+
when /^geometry$/i then :geometry
|
171
|
+
when /multipoint/i then :multi_point
|
172
|
+
when /multilinestring/i then :multi_line_string
|
173
|
+
when /multipolygon/i then :multi_polygon
|
174
|
+
when /geometrycollection/i then :geometry_collection
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
|
179
|
+
end
|