lib/spatial_adapter/adapters/postgis.rb in spatial_adapter-0.2.1 vs lib/spatial_adapter/adapters/postgis.rb in spatial_adapter-0.3.0
- old
+ new
@@ -23,11 +23,11 @@
def spatial?
!postgis_version.nil?
end
- def supports_geography?
+ def supports_geographic?
postgis_major_version > 1 || (postgis_major_version == 1 && postgis_minor_version >= 5)
end
alias :original_native_database_types :native_database_types
def native_database_types
@@ -47,13 +47,16 @@
def columns(table_name, name = nil) #:nodoc:
raw_geom_infos = column_spatial_info(table_name)
column_definitions(table_name).collect do |name, type, default, notnull|
case type
+ when /geography/i
+ ActiveRecord::ConnectionAdapters::SpatialPostgreSQLColumn.create_from_geography(name, default, type, notnull == 'f')
when /geometry/i
raw_geom_info = raw_geom_infos[name]
if raw_geom_info.nil?
+ # This column isn't in the geometry_columns table, so we don't know anything else about it
ActiveRecord::ConnectionAdapters::SpatialPostgreSQLColumn.create_simplified(name, default, notnull == "f")
else
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)
end
else
@@ -75,40 +78,53 @@
create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
create_sql << "#{quote_table_name(table_name)} ("
create_sql << table_definition.to_sql
create_sql << ") #{options[:options]}"
- execute create_sql
# This is the additional portion for PostGIS
unless table_definition.geom_columns.nil?
table_definition.geom_columns.each do |geom_column|
- execute geom_column.to_sql(table_name)
+ geom_column.table_name = table_name
+ create_sql << "; " + geom_column.to_sql
end
end
+
+ execute create_sql
end
alias :original_remove_column :remove_column
- def remove_column(table_name, column_name, options = {})
+ def remove_column(table_name, *column_names)
+ column_names = column_names.flatten
columns(table_name).each do |col|
- if col.name == column_name.to_s
- #check if the column is geometric
- unless geometry_data_types[col.type].nil? or
- (options[:remove_using_dropgeometrycolumn] == false)
- execute "SELECT DropGeometryColumn('#{table_name}','#{column_name}')"
+ if column_names.include?(col.name.to_sym)
+ # Geometry columns have to be removed using DropGeometryColumn
+ if col.type == :geometry && !col.geographic?
+ execute "SELECT DropGeometryColumn('#{table_name}','#{col.name}')"
else
- original_remove_column(table_name, column_name)
+ original_remove_column(table_name, col.name)
end
end
end
end
alias :original_add_column :add_column
def add_column(table_name, column_name, type, options = {})
- unless geometry_data_types[type].nil? or (options[:create_using_addgeometrycolumn] == false)
- 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)
- execute geom_column.to_sql(table_name)
+ unless geometry_data_types[type].nil?
+ 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)
+ if geom_column.geographic
+ default = options[:default]
+ notnull = options[:null] == false
+
+ execute("ALTER TABLE #{quote_table_name(table_name)} ADD COLUMN #{geom_column.to_sql}")
+
+ change_column_default(table_name, column_name, default) if options_include_default?(options)
+ change_column_null(table_name, column_name, false, default) if notnull
+ else
+ geom_column.table_name = table_name
+ execute geom_column.to_sql
+ end
else
original_add_column(table_name, column_name, type, options)
end
end
@@ -226,77 +242,141 @@
def column(name, type, options = {})
unless (@base.geometry_data_types[type.to_sym].nil? or
(options[:create_using_addgeometrycolumn] == false))
- geom_column = PostgreSQLColumnDefinition.new(@base, name, type)
- geom_column.null = options[:null]
- geom_column.srid = options[:srid] || -1
- geom_column.with_z = options[:with_z] || false
- geom_column.with_m = options[:with_m] || false
-
- @geom_columns ||= []
- @geom_columns << geom_column
+ column = self[name] || PostgreSQLColumnDefinition.new(@base, name, type)
+ column.null = options[:null]
+ column.srid = options[:srid] || -1
+ column.with_z = options[:with_z] || false
+ column.with_m = options[:with_m] || false
+ column.geographic = options[:geographic] || false
+
+ if column.geographic
+ @columns << column unless @columns.include? column
+ else
+ # Hold this column for later
+ @geom_columns ||= []
+ @geom_columns << column
+ end
+ self
else
super(name, type, options)
end
end
end
class PostgreSQLColumnDefinition < ColumnDefinition
- attr_accessor :srid, :with_z, :with_m
+ attr_accessor :table_name
+ attr_accessor :srid, :with_z, :with_m, :geographic
attr_reader :spatial
- def initialize(base = nil, name = nil, type=nil, limit=nil, default=nil, null=nil, srid=-1, with_z=false, with_m=false)
- super(base, name, type, limit, default,null)
+ def initialize(base = nil, name = nil, type=nil, limit=nil, default=nil, null=nil, srid=-1, with_z=false, with_m=false, geographic=false)
+ super(base, name, type, limit, default, null)
+ @table_name = nil
@spatial = true
@srid = srid
@with_z = with_z
@with_m = with_m
+ @geographic = geographic
end
- def to_sql(table_name)
- if @spatial
- type_sql = type_to_sql(type.to_sym)
+ def sql_type
+ if geographic
+ type_sql = base.geometry_data_types[type.to_sym][:name]
+ type_sql += "Z" if with_z
+ type_sql += "M" if with_m
+ # SRID is not yet supported (defaults to 4326)
+ #type_sql += ", #{srid}" if (srid && srid != -1)
+ type_sql = "geography(#{type_sql})"
+ type_sql
+ else
+ super
+ end
+ end
+
+ def to_sql
+ if spatial && !geographic
+ type_sql = base.geometry_data_types[type.to_sym][:name]
type_sql += "M" if with_m and !with_z
if with_m and with_z
dimension = 4
elsif with_m or with_z
dimension = 3
else
dimension = 2
end
-
+
column_sql = "SELECT AddGeometryColumn('#{table_name}','#{name}',#{srid},'#{type_sql}',#{dimension})"
column_sql += ";ALTER TABLE #{table_name} ALTER #{name} SET NOT NULL" if null == false
column_sql
else
super
end
end
-
- private
-
- def type_to_sql(name, limit=nil)
- base.type_to_sql(name, limit) rescue name
- end
end
end
end
module ActiveRecord
module ConnectionAdapters
class SpatialPostgreSQLColumn < PostgreSQLColumn
include SpatialAdapter::SpatialColumn
+ def initialize(name, default, sql_type = nil, null = true, srid=-1, with_z=false, with_m=false, geographic = false)
+ super(name, default, sql_type, null, srid, with_z, with_m)
+ @geographic = geographic
+ end
+
+ def geographic?
+ @geographic
+ end
+
#Transforms a string to a geometry. PostGIS returns a HewEWKB string.
def self.string_to_geometry(string)
return string unless string.is_a?(String)
GeoRuby::SimpleFeatures::Geometry.from_hex_ewkb(string) rescue nil
end
- def self.create_simplified(name,default,null = true)
- new(name,default,"geometry",null,nil,nil,nil)
+ def self.create_simplified(name, default, null = true)
+ new(name, default, "geometry", null)
+ end
+
+ def self.create_from_geography(name, default, sql_type, null = true)
+ params = extract_geography_params(sql_type)
+ new(name, default, sql_type, null, params[:srid], params[:with_z], params[:with_m], true)
+ end
+
+ private
+
+ # Add detection of PostGIS-specific geography columns
+ def geometry_simplified_type(sql_type)
+ case sql_type
+ when /geography\(point/i then :point
+ when /geography\(linestring/i then :line_string
+ when /geography\(polygon/i then :polygon
+ when /geography\(multipoint/i then :multi_point
+ when /geography\(multilinestring/i then :multi_line_string
+ when /geography\(multipolygon/i then :multi_polygon
+ when /geography\(geometrycollection/i then :geometry_collection
+ when /geography/i then :geometry
+ else
+ super
+ end
+ end
+
+ def self.extract_geography_params(sql_type)
+ params = {
+ :srid => 0,
+ :with_z => false,
+ :with_m => false
+ }
+ if sql_type =~ /geography(?:\((?:\w+?)(Z)?(M)?(?:,(\d+))?\))?/i
+ params[:with_z] = $1 == 'Z'
+ params[:with_m] = $2 == 'M'
+ params[:srid] = $3.to_i
+ end
+ params
end
end
end
end