spatial_adapter 1.2.0 → 1.3.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/.gitignore +3 -0
- data/Gemfile +3 -0
- data/README.rdoc +30 -17
- data/Rakefile +11 -0
- data/init.rb +1 -0
- data/lib/spatial_adapter/base/mysql/adapter.rb +54 -0
- data/lib/spatial_adapter/base/mysql/spatial_column.rb +12 -0
- data/lib/spatial_adapter/base/mysql.rb +9 -0
- data/lib/spatial_adapter/common/schema_dumper.rb +12 -12
- data/lib/spatial_adapter/common/spatial_column.rb +4 -4
- data/lib/spatial_adapter/common/table_definition.rb +8 -10
- data/lib/spatial_adapter/common.rb +10 -0
- data/lib/spatial_adapter/jdbcmysql.rb +50 -0
- data/lib/spatial_adapter/mysql.rb +43 -85
- data/lib/spatial_adapter/mysql2.rb +39 -86
- data/lib/spatial_adapter/postgresql.rb +315 -324
- data/lib/spatial_adapter/version.rb +3 -0
- data/lib/spatial_adapter.rb +3 -7
- data/spatial_adapter.gemspec +39 -0
- data/spec/db/jdbcmysql_raw.rb +70 -0
- data/spec/db/mysql2_raw.rb +9 -9
- data/spec/db/mysql_raw.rb +9 -9
- data/spec/jdbcmysql_spec.rb +25 -0
- data/spec/mysql2_spec.rb +31 -0
- data/spec/mysql_spec.rb +17 -0
- data/spec/postgresql/connection_adapter_spec.rb +34 -39
- data/spec/postgresql/migration_spec.rb +51 -51
- data/spec/postgresql/models_spec.rb +37 -37
- data/spec/postgresql/schema_dumper_spec.rb +12 -12
- data/spec/postgresql_spec.rb +5 -0
- data/spec/{shared_examples.rb → shared/common_model_actions_spec.rb} +2 -2
- data/spec/shared/mysql_connection_adapter_spec.rb +110 -0
- data/spec/{mysql/migration_spec.rb → shared/mysql_migration_spec.rb} +17 -17
- data/spec/shared/mysql_models_spec.rb +58 -0
- data/spec/{mysql/schema_dumper_spec.rb → shared/mysql_schema_dumper_spec.rb} +24 -27
- data/spec/spec_helper.rb +25 -21
- metadata +131 -84
- data/VERSION +0 -1
- data/spec/README.txt +0 -22
- data/spec/mysql/connection_adapter_spec.rb +0 -106
- data/spec/mysql/models_spec.rb +0 -65
- data/spec/mysql2/connection_adapter_spec.rb +0 -106
- data/spec/mysql2/migration_spec.rb +0 -64
- data/spec/mysql2/models_spec.rb +0 -65
- data/spec/mysql2/schema_dumper_spec.rb +0 -56
@@ -1,388 +1,379 @@
|
|
1
1
|
require 'spatial_adapter'
|
2
2
|
require 'active_record/connection_adapters/postgresql_adapter'
|
3
3
|
|
4
|
-
ActiveRecord::ConnectionAdapters
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
4
|
+
module ActiveRecord::ConnectionAdapters
|
5
|
+
class PostgreSQLAdapter
|
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
12
|
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def postgis_major_version
|
16
|
-
version = postgis_version
|
17
|
-
version ? version.scan(/^(\d)\.\d\.\d$/)[0][0].to_i : nil
|
18
|
-
end
|
19
|
-
|
20
|
-
def postgis_minor_version
|
21
|
-
version = postgis_version
|
22
|
-
version ? version.scan(/^\d\.(\d)\.\d$/)[0][0].to_i : nil
|
23
|
-
end
|
24
|
-
|
25
|
-
def spatial?
|
26
|
-
!postgis_version.nil?
|
27
|
-
end
|
28
|
-
|
29
|
-
def supports_geographic?
|
30
|
-
postgis_major_version > 1 || (postgis_major_version == 1 && postgis_minor_version >= 5)
|
31
|
-
end
|
32
|
-
|
33
|
-
alias :original_native_database_types :native_database_types
|
34
|
-
def native_database_types
|
35
|
-
original_native_database_types.merge!(geometry_data_types)
|
36
|
-
end
|
37
13
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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_geographic?
|
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!(SpatialAdapter.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
45
|
end
|
46
|
-
end
|
47
46
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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 /geography/i
|
53
|
+
ActiveRecord::ConnectionAdapters::SpatialPostgreSQLColumn.create_from_geography(name, default, type, notnull == 'f')
|
54
|
+
when /geometry/i
|
55
|
+
raw_geom_info = raw_geom_infos[name]
|
56
|
+
if raw_geom_info.nil?
|
57
|
+
# This column isn't in the geometry_columns table, so we don't know anything else about it
|
58
|
+
ActiveRecord::ConnectionAdapters::SpatialPostgreSQLColumn.create_simplified(name, default, notnull == "f")
|
59
|
+
else
|
60
|
+
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)
|
61
|
+
end
|
60
62
|
else
|
61
|
-
ActiveRecord::ConnectionAdapters::
|
63
|
+
ActiveRecord::ConnectionAdapters::PostgreSQLColumn.new(name, default, type, notnull == "f")
|
62
64
|
end
|
63
|
-
else
|
64
|
-
ActiveRecord::ConnectionAdapters::PostgreSQLColumn.new(name, default, type, notnull == "f")
|
65
65
|
end
|
66
66
|
end
|
67
|
-
end
|
68
67
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
68
|
+
def create_table(table_name, options = {})
|
69
|
+
# Using the subclassed table definition
|
70
|
+
table_definition = ActiveRecord::ConnectionAdapters::PostgreSQLTableDefinition.new(self)
|
71
|
+
table_definition.primary_key(options[:primary_key] || ActiveRecord::Base.get_primary_key(table_name.to_s.singularize)) unless options[:id] == false
|
73
72
|
|
74
|
-
|
73
|
+
yield table_definition if block_given?
|
75
74
|
|
76
|
-
|
77
|
-
|
78
|
-
|
75
|
+
if options[:force] && table_exists?(table_name)
|
76
|
+
drop_table(table_name, options)
|
77
|
+
end
|
79
78
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
79
|
+
create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
|
80
|
+
create_sql << "#{quote_table_name(table_name)} ("
|
81
|
+
create_sql << table_definition.to_sql
|
82
|
+
create_sql << ") #{options[:options]}"
|
84
83
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
84
|
+
# This is the additional portion for PostGIS
|
85
|
+
unless table_definition.geom_columns.nil?
|
86
|
+
table_definition.geom_columns.each do |geom_column|
|
87
|
+
geom_column.table_name = table_name
|
88
|
+
create_sql << "; " + geom_column.to_sql
|
89
|
+
end
|
90
90
|
end
|
91
|
-
end
|
92
91
|
|
93
|
-
|
94
|
-
|
92
|
+
execute create_sql
|
93
|
+
end
|
95
94
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
95
|
+
alias :original_remove_column :remove_column
|
96
|
+
def remove_column(table_name, *column_names)
|
97
|
+
column_names = column_names.flatten
|
98
|
+
columns(table_name).each do |col|
|
99
|
+
if column_names.include?(col.name.to_sym)
|
100
|
+
# Geometry columns have to be removed using DropGeometryColumn
|
101
|
+
if col.is_a?(::SpatialAdapter::SpatialColumn) && col.spatial? && !col.geographic?
|
102
|
+
execute "SELECT DropGeometryColumn('#{table_name}','#{col.name}')"
|
103
|
+
else
|
104
|
+
original_remove_column(table_name, col.name)
|
105
|
+
end
|
106
106
|
end
|
107
107
|
end
|
108
108
|
end
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
109
|
+
|
110
|
+
alias :original_add_column :add_column
|
111
|
+
def add_column(table_name, column_name, type, options = {})
|
112
|
+
unless SpatialAdapter.geometry_data_types[type].nil?
|
113
|
+
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, options[:geographic] || false)
|
114
|
+
if geom_column.geographic
|
115
|
+
default = options[:default]
|
116
|
+
notnull = options[:null] == false
|
117
|
+
|
118
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} ADD COLUMN #{geom_column.to_sql}")
|
119
|
+
|
120
|
+
change_column_default(table_name, column_name, default) if options_include_default?(options)
|
121
|
+
change_column_null(table_name, column_name, false, default) if notnull
|
122
|
+
else
|
123
|
+
geom_column.table_name = table_name
|
124
|
+
execute geom_column.to_sql
|
125
|
+
end
|
123
126
|
else
|
124
|
-
|
125
|
-
execute geom_column.to_sql
|
127
|
+
original_add_column(table_name, column_name, type, options)
|
126
128
|
end
|
127
|
-
else
|
128
|
-
original_add_column(table_name, column_name, type, options)
|
129
129
|
end
|
130
|
-
end
|
131
130
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
131
|
+
# Adds an index to a column.
|
132
|
+
def add_index(table_name, column_name, options = {})
|
133
|
+
column_names = Array(column_name)
|
134
|
+
index_name = index_name(table_name, :column => column_names)
|
135
|
+
|
136
|
+
if Hash === options # legacy support, since this param was a string
|
137
|
+
index_type = options[:unique] ? "UNIQUE" : ""
|
138
|
+
index_name = options[:name] || index_name
|
139
|
+
index_method = options[:spatial] ? 'USING GIST' : ""
|
140
|
+
else
|
141
|
+
index_type = options
|
142
|
+
end
|
143
|
+
quoted_column_names = column_names.map { |e| quote_column_name(e) }.join(", ")
|
144
|
+
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_method} (#{quoted_column_names})"
|
143
145
|
end
|
144
|
-
quoted_column_names = column_names.map { |e| quote_column_name(e) }.join(", ")
|
145
|
-
execute "CREATE #{index_type} INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)} #{index_method} (#{quoted_column_names})"
|
146
|
-
end
|
147
146
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
SQL
|
169
|
-
|
170
|
-
|
171
|
-
indexes = []
|
172
|
-
|
173
|
-
indexes = result.map do |row|
|
174
|
-
index_name = row[0]
|
175
|
-
unique = row[1] == 't'
|
176
|
-
indkey = row[2].split(" ")
|
177
|
-
oid = row[3]
|
178
|
-
indtype = row[4]
|
179
|
-
|
180
|
-
# Changed from upstream: need to get the column types to test for spatial indexes
|
181
|
-
columns = query(<<-SQL, "Columns for index #{row[0]} on #{table_name}").inject({}) {|attlist, r| attlist[r[1]] = [r[0], r[2]]; attlist}
|
182
|
-
SELECT a.attname, a.attnum, t.typname
|
183
|
-
FROM pg_attribute a, pg_type t
|
184
|
-
WHERE a.attrelid = #{oid}
|
185
|
-
AND a.attnum IN (#{indkey.join(",")})
|
186
|
-
AND a.atttypid = t.oid
|
147
|
+
# Returns the list of all indexes for a table.
|
148
|
+
#
|
149
|
+
# This is a full replacement for the ActiveRecord method and as a result
|
150
|
+
# has a higher probability of breaking in future releases.
|
151
|
+
def indexes(table_name, name = nil)
|
152
|
+
schemas = schema_search_path.split(/,/).map { |p| quote(p) }.join(',')
|
153
|
+
|
154
|
+
# Changed from upstread: link to pg_am to grab the index type (e.g. "gist")
|
155
|
+
result = query(<<-SQL, name)
|
156
|
+
SELECT distinct i.relname, d.indisunique, d.indkey, t.oid, am.amname
|
157
|
+
FROM pg_class t, pg_class i, pg_index d, pg_attribute a, pg_am am
|
158
|
+
WHERE i.relkind = 'i'
|
159
|
+
AND d.indexrelid = i.oid
|
160
|
+
AND d.indisprimary = 'f'
|
161
|
+
AND t.oid = d.indrelid
|
162
|
+
AND t.relname = '#{table_name}'
|
163
|
+
AND i.relnamespace IN (SELECT oid FROM pg_namespace WHERE nspname IN (#{schemas}) )
|
164
|
+
AND i.relam = am.oid
|
165
|
+
AND a.attrelid = t.oid
|
166
|
+
ORDER BY i.relname
|
187
167
|
SQL
|
188
168
|
|
189
|
-
# Only GiST indexes on spatial columns denote a spatial index
|
190
|
-
spatial = indtype == 'gist' && columns.size == 1 && (columns.values.first[1] == 'geometry' || columns.values.first[1] == 'geography')
|
191
169
|
|
192
|
-
|
193
|
-
ActiveRecord::ConnectionAdapters::IndexDefinition.new(table_name, index_name, unique, column_names, spatial)
|
194
|
-
end
|
170
|
+
indexes = []
|
195
171
|
|
196
|
-
|
197
|
-
|
172
|
+
indexes = result.map do |row|
|
173
|
+
index_name = row[0]
|
174
|
+
unique = row[1] == 't'
|
175
|
+
indkey = row[2].split(" ")
|
176
|
+
oid = row[3]
|
177
|
+
indtype = row[4]
|
198
178
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
179
|
+
# Changed from upstream: need to get the column types to test for spatial indexes
|
180
|
+
columns = query(<<-SQL, "Columns for index #{row[0]} on #{table_name}").inject({}) {|attlist, r| attlist[r[1]] = [r[0], r[2]]; attlist}
|
181
|
+
SELECT a.attname, a.attnum, t.typname
|
182
|
+
FROM pg_attribute a, pg_type t
|
183
|
+
WHERE a.attrelid = #{oid}
|
184
|
+
AND a.attnum IN (#{indkey.join(",")})
|
185
|
+
AND a.atttypid = t.oid
|
186
|
+
SQL
|
187
|
+
|
188
|
+
# Only GiST indexes on spatial columns denote a spatial index
|
189
|
+
spatial = indtype == 'gist' && columns.size == 1 && (columns.values.first[1] == 'geometry' || columns.values.first[1] == 'geography')
|
190
|
+
|
191
|
+
column_names = indkey.map {|attnum| columns[attnum] ? columns[attnum][0] : nil }
|
192
|
+
ActiveRecord::ConnectionAdapters::IndexDefinition.new(table_name, index_name, unique, column_names, spatial)
|
193
|
+
end
|
194
|
+
|
195
|
+
indexes
|
207
196
|
end
|
208
|
-
end
|
209
197
|
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
raw_geom_infos = {}
|
220
|
-
constr.each do |constr_def_a|
|
221
|
-
raw_geom_infos[constr_def_a[3]] ||= SpatialAdapter::RawGeomInfo.new
|
222
|
-
raw_geom_infos[constr_def_a[3]].type = constr_def_a[6]
|
223
|
-
raw_geom_infos[constr_def_a[3]].dimension = constr_def_a[4].to_i
|
224
|
-
raw_geom_infos[constr_def_a[3]].srid = constr_def_a[5].to_i
|
225
|
-
|
226
|
-
if raw_geom_infos[constr_def_a[3]].type[-1] == ?M
|
227
|
-
raw_geom_infos[constr_def_a[3]].with_m = true
|
228
|
-
raw_geom_infos[constr_def_a[3]].type.chop!
|
229
|
-
else
|
230
|
-
raw_geom_infos[constr_def_a[3]].with_m = false
|
198
|
+
def disable_referential_integrity(&block) #:nodoc:
|
199
|
+
if supports_disable_referential_integrity?() then
|
200
|
+
execute(tables_without_postgis.collect { |name| "ALTER TABLE #{quote_table_name(name)} DISABLE TRIGGER ALL" }.join(";"))
|
201
|
+
end
|
202
|
+
yield
|
203
|
+
ensure
|
204
|
+
if supports_disable_referential_integrity?() then
|
205
|
+
execute(tables_without_postgis.collect { |name| "ALTER TABLE #{quote_table_name(name)} ENABLE TRIGGER ALL" }.join(";"))
|
231
206
|
end
|
232
207
|
end
|
233
208
|
|
234
|
-
|
235
|
-
|
236
|
-
|
209
|
+
private
|
210
|
+
|
211
|
+
def tables_without_postgis
|
212
|
+
tables - %w{ geometry_columns spatial_ref_sys }
|
237
213
|
end
|
238
214
|
|
239
|
-
|
215
|
+
def column_spatial_info(table_name)
|
216
|
+
constr = query("SELECT * FROM geometry_columns WHERE f_table_name = '#{table_name}'")
|
240
217
|
|
241
|
-
|
242
|
-
|
218
|
+
raw_geom_infos = {}
|
219
|
+
constr.each do |constr_def_a|
|
220
|
+
raw_geom_infos[constr_def_a[3]] ||= SpatialAdapter::RawGeomInfo.new
|
221
|
+
raw_geom_infos[constr_def_a[3]].type = constr_def_a[6]
|
222
|
+
raw_geom_infos[constr_def_a[3]].dimension = constr_def_a[4].to_i
|
223
|
+
raw_geom_infos[constr_def_a[3]].srid = constr_def_a[5].to_i
|
243
224
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
attr_reader :geom_columns
|
248
|
-
|
249
|
-
def column(name, type, options = {})
|
250
|
-
unless (@base.geometry_data_types[type.to_sym].nil? or
|
251
|
-
(options[:create_using_addgeometrycolumn] == false))
|
252
|
-
|
253
|
-
column = self[name] || PostgreSQLColumnDefinition.new(@base, name, type)
|
254
|
-
column.null = options[:null]
|
255
|
-
column.srid = options[:srid] || -1
|
256
|
-
column.with_z = options[:with_z] || false
|
257
|
-
column.with_m = options[:with_m] || false
|
258
|
-
column.geographic = options[:geographic] || false
|
259
|
-
|
260
|
-
if column.geographic
|
261
|
-
@columns << column unless @columns.include? column
|
262
|
-
else
|
263
|
-
# Hold this column for later
|
264
|
-
@geom_columns ||= []
|
265
|
-
@geom_columns << column
|
266
|
-
end
|
267
|
-
self
|
225
|
+
if raw_geom_infos[constr_def_a[3]].type[-1] == ?M
|
226
|
+
raw_geom_infos[constr_def_a[3]].with_m = true
|
227
|
+
raw_geom_infos[constr_def_a[3]].type.chop!
|
268
228
|
else
|
269
|
-
|
229
|
+
raw_geom_infos[constr_def_a[3]].with_m = false
|
270
230
|
end
|
271
|
-
end
|
272
|
-
end
|
231
|
+
end
|
273
232
|
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
attr_reader :spatial
|
278
|
-
|
279
|
-
def initialize(base = nil, name = nil, type=nil, limit=nil, default=nil, null=nil, srid=-1, with_z=false, with_m=false, geographic=false)
|
280
|
-
super(base, name, type, limit, default, null)
|
281
|
-
@table_name = nil
|
282
|
-
@spatial = true
|
283
|
-
@srid = srid
|
284
|
-
@with_z = with_z
|
285
|
-
@with_m = with_m
|
286
|
-
@geographic = geographic
|
233
|
+
raw_geom_infos.each_value do |raw_geom_info|
|
234
|
+
#check the presence of z and m
|
235
|
+
raw_geom_info.convert!
|
287
236
|
end
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
237
|
+
|
238
|
+
raw_geom_infos
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
class PostgreSQLTableDefinition < TableDefinition
|
243
|
+
attr_reader :geom_columns
|
244
|
+
|
245
|
+
def column(name, type, options = {})
|
246
|
+
unless (SpatialAdapter.geometry_data_types[type.to_sym].nil? or
|
247
|
+
(options[:create_using_addgeometrycolumn] == false))
|
248
|
+
|
249
|
+
column = self[name] || PostgreSQLColumnDefinition.new(@base, name, type)
|
250
|
+
column.null = options[:null]
|
251
|
+
column.srid = options[:srid] || -1
|
252
|
+
column.with_z = options[:with_z] || false
|
253
|
+
column.with_m = options[:with_m] || false
|
254
|
+
column.geographic = options[:geographic] || false
|
255
|
+
|
256
|
+
if column.geographic
|
257
|
+
@columns << column unless @columns.include? column
|
298
258
|
else
|
299
|
-
|
259
|
+
# Hold this column for later
|
260
|
+
@geom_columns ||= []
|
261
|
+
@geom_columns << column
|
300
262
|
end
|
263
|
+
self
|
264
|
+
else
|
265
|
+
super(name, type, options)
|
301
266
|
end
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
class PostgreSQLColumnDefinition < ColumnDefinition
|
271
|
+
attr_accessor :table_name
|
272
|
+
attr_accessor :srid, :with_z, :with_m, :geographic
|
273
|
+
attr_reader :spatial
|
274
|
+
|
275
|
+
def initialize(base = nil, name = nil, type=nil, limit=nil, default=nil, null=nil, srid=-1, with_z=false, with_m=false, geographic=false)
|
276
|
+
super(base, name, type, limit, default, null)
|
277
|
+
@table_name = nil
|
278
|
+
@spatial = true
|
279
|
+
@srid = srid
|
280
|
+
@with_z = with_z
|
281
|
+
@with_m = with_m
|
282
|
+
@geographic = geographic
|
283
|
+
end
|
284
|
+
|
285
|
+
def sql_type
|
286
|
+
if geographic
|
287
|
+
type_sql = SpatialAdapter.geometry_data_types[type.to_sym][:name]
|
288
|
+
type_sql += "Z" if with_z
|
289
|
+
type_sql += "M" if with_m
|
290
|
+
# SRID is not yet supported (defaults to 4326)
|
291
|
+
#type_sql += ", #{srid}" if (srid && srid != -1)
|
292
|
+
type_sql = "geography(#{type_sql})"
|
293
|
+
type_sql
|
294
|
+
else
|
295
|
+
super
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
def to_sql
|
300
|
+
if spatial && !geographic
|
301
|
+
type_sql = SpatialAdapter.geometry_data_types[type.to_sym][:name]
|
302
|
+
type_sql += "M" if with_m and !with_z
|
303
|
+
if with_m and with_z
|
304
|
+
dimension = 4
|
305
|
+
elsif with_m or with_z
|
306
|
+
dimension = 3
|
318
307
|
else
|
319
|
-
|
308
|
+
dimension = 2
|
320
309
|
end
|
310
|
+
|
311
|
+
column_sql = "SELECT AddGeometryColumn('#{table_name}','#{name}',#{srid},'#{type_sql}',#{dimension})"
|
312
|
+
column_sql += ";ALTER TABLE #{table_name} ALTER #{name} SET NOT NULL" if null == false
|
313
|
+
column_sql
|
314
|
+
else
|
315
|
+
super
|
321
316
|
end
|
322
317
|
end
|
323
318
|
end
|
324
|
-
end
|
325
319
|
|
326
|
-
|
327
|
-
|
328
|
-
class SpatialPostgreSQLColumn < PostgreSQLColumn
|
329
|
-
include SpatialAdapter::SpatialColumn
|
320
|
+
class SpatialPostgreSQLColumn < PostgreSQLColumn
|
321
|
+
include SpatialAdapter::SpatialColumn
|
330
322
|
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
323
|
+
def initialize(name, default, sql_type = nil, null = true, srid=-1, with_z=false, with_m=false, geographic = false)
|
324
|
+
super(name, default, sql_type, null, srid, with_z, with_m)
|
325
|
+
@geographic = geographic
|
326
|
+
end
|
335
327
|
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
#Transforms a string to a geometry. PostGIS returns a HewEWKB string.
|
341
|
-
def self.string_to_geometry(string)
|
342
|
-
return string unless string.is_a?(String)
|
343
|
-
GeoRuby::SimpleFeatures::Geometry.from_hex_ewkb(string) rescue nil
|
344
|
-
end
|
328
|
+
def geographic?
|
329
|
+
@geographic
|
330
|
+
end
|
345
331
|
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
332
|
+
#Transforms a string to a geometry. PostGIS returns a HewEWKB string.
|
333
|
+
def self.string_to_geometry(string)
|
334
|
+
return string unless string.is_a?(String)
|
335
|
+
GeoRuby::SimpleFeatures::Geometry.from_hex_ewkb(string) rescue nil
|
336
|
+
end
|
337
|
+
|
338
|
+
def self.create_simplified(name, default, null = true)
|
339
|
+
new(name, default, "geometry", null)
|
340
|
+
end
|
341
|
+
|
342
|
+
def self.create_from_geography(name, default, sql_type, null = true)
|
343
|
+
params = extract_geography_params(sql_type)
|
344
|
+
new(name, default, sql_type, null, params[:srid], params[:with_z], params[:with_m], true)
|
345
|
+
end
|
346
|
+
|
347
|
+
private
|
348
|
+
|
349
|
+
# Add detection of PostGIS-specific geography columns
|
350
|
+
def geometry_simplified_type(sql_type)
|
351
|
+
case sql_type
|
352
|
+
when /geography\(point/i then :point
|
353
|
+
when /geography\(linestring/i then :line_string
|
354
|
+
when /geography\(polygon/i then :polygon
|
355
|
+
when /geography\(multipoint/i then :multi_point
|
356
|
+
when /geography\(multilinestring/i then :multi_line_string
|
357
|
+
when /geography\(multipolygon/i then :multi_polygon
|
358
|
+
when /geography\(geometrycollection/i then :geometry_collection
|
359
|
+
when /geography/i then :geometry
|
360
|
+
else
|
361
|
+
super
|
371
362
|
end
|
363
|
+
end
|
372
364
|
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
end
|
384
|
-
params
|
365
|
+
def self.extract_geography_params(sql_type)
|
366
|
+
params = {
|
367
|
+
:srid => 0,
|
368
|
+
:with_z => false,
|
369
|
+
:with_m => false
|
370
|
+
}
|
371
|
+
if sql_type =~ /geography(?:\((?:\w+?)(Z)?(M)?(?:,(\d+))?\))?/i
|
372
|
+
params[:with_z] = $1 == 'Z'
|
373
|
+
params[:with_m] = $2 == 'M'
|
374
|
+
params[:srid] = $3.to_i
|
385
375
|
end
|
376
|
+
params
|
386
377
|
end
|
387
378
|
end
|
388
379
|
end
|