spatial_adapter 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,23 @@
1
+ module SpatialAdapter
2
+ class RawGeomInfo < Struct.new(:type,:srid,:dimension,:with_z,:with_m) #:nodoc:
3
+ def convert!
4
+ self.type = "geometry" if self.type.nil? #if geometry the geometrytype constraint is not present : need to set the type here then
5
+
6
+ if dimension == 4
7
+ self.with_m = true
8
+ self.with_z = true
9
+ elsif dimension == 3
10
+ if with_m
11
+ self.with_z = false
12
+ self.with_m = true
13
+ else
14
+ self.with_z = true
15
+ self.with_m = false
16
+ end
17
+ else
18
+ self.with_z = false
19
+ self.with_m = false
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,11 @@
1
+ require 'active_record/connection_adapters/abstract_adapter'
2
+
3
+ ActiveRecord::ConnectionAdapters::IndexDefinition.class_eval do
4
+ attr_accessor :spatial
5
+
6
+ alias_method :initialize_without_spatial, :initialize
7
+ def initialize(table, name, unique, columns, spatial = false)
8
+ initialize_without_spatial(table, name, unique, columns)
9
+ @spatial = spatial
10
+ end
11
+ end
@@ -0,0 +1,135 @@
1
+ ActiveRecord::SchemaDumper.ignore_tables << "spatial_ref_sys" << "geometry_columns"
2
+
3
+ ActiveRecord::SchemaDumper.class_eval do
4
+ # These are the valid options for a column specification (spatial options added)
5
+ VALID_COLUMN_SPEC_KEYS = [:name, :limit, :precision, :scale, :default, :null, :srid, :with_z, :with_m]
6
+
7
+ def table(table, stream)
8
+ columns = @connection.columns(table)
9
+ begin
10
+ tbl = StringIO.new
11
+
12
+ # first dump primary key column
13
+ if @connection.respond_to?(:pk_and_sequence_for)
14
+ pk, pk_seq = @connection.pk_and_sequence_for(table)
15
+ elsif @connection.respond_to?(:primary_key)
16
+ pk = @connection.primary_key(table)
17
+ end
18
+
19
+ tbl.print " create_table #{table.inspect}"
20
+ if columns.detect { |c| c.name == pk }
21
+ if pk != 'id'
22
+ tbl.print %Q(, :primary_key => "#{pk}")
23
+ end
24
+ else
25
+ tbl.print ", :id => false"
26
+ end
27
+
28
+ # Added by Spatial Adapter to ensure correct MySQL table engine
29
+ if @connection.respond_to?(:options_for)
30
+ res = @connection.options_for(table)
31
+ tbl.print ", :options=>'#{res}'" if res
32
+ end
33
+
34
+ tbl.print ", :force => true"
35
+ tbl.puts " do |t|"
36
+
37
+ # then dump all non-primary key columns
38
+ column_specs = columns.map do |column|
39
+ raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" if @types[column.type].nil?
40
+ next if column.name == pk
41
+ spec = column_spec(column)
42
+ (spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k.inspect} => ")}
43
+ spec
44
+ end.compact
45
+
46
+ # find all migration keys used in this table
47
+ keys = VALID_COLUMN_SPEC_KEYS & column_specs.map(&:keys).flatten
48
+
49
+ # figure out the lengths for each column based on above keys
50
+ lengths = keys.map{ |key| column_specs.map{ |spec| spec[key] ? spec[key].length + 2 : 0 }.max }
51
+
52
+ # the string we're going to sprintf our values against, with standardized column widths
53
+ format_string = lengths.map{ |len| "%-#{len}s" }
54
+
55
+ # find the max length for the 'type' column, which is special
56
+ type_length = column_specs.map{ |column| column[:type].length }.max
57
+
58
+ # add column type definition to our format string
59
+ format_string.unshift " t.%-#{type_length}s "
60
+
61
+ format_string *= ''
62
+
63
+ column_specs.each do |colspec|
64
+ values = keys.zip(lengths).map{ |key, len| colspec.key?(key) ? colspec[key] + ", " : " " * len }
65
+ values.unshift colspec[:type]
66
+ tbl.print((format_string % values).gsub(/,\s*$/, ''))
67
+ tbl.puts
68
+ end
69
+
70
+ tbl.puts " end"
71
+ tbl.puts
72
+
73
+ indexes(table, tbl)
74
+
75
+ tbl.rewind
76
+ stream.print tbl.read
77
+ rescue => e
78
+ stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
79
+ stream.puts "# #{e.message}"
80
+ stream.puts
81
+ end
82
+
83
+ stream
84
+ end
85
+
86
+
87
+ def indexes(table, stream)
88
+ if (indexes = @connection.indexes(table)).any?
89
+ add_index_statements = indexes.map do |index|
90
+ statment_parts = [ ('add_index ' + index.table.inspect) ]
91
+ statment_parts << index.columns.inspect
92
+ statment_parts << (':name => ' + index.name.inspect)
93
+ statment_parts << ':unique => true' if index.unique
94
+ # Add spatial option (this is the only change from the original method)
95
+ statment_parts << ':spatial => true' if index.spatial
96
+
97
+ ' ' + statment_parts.join(', ')
98
+ end
99
+
100
+ stream.puts add_index_statements.sort.join("\n")
101
+ stream.puts
102
+ end
103
+ end
104
+
105
+ private
106
+
107
+ # Build specification for a table column
108
+ def column_spec(column)
109
+ spec = {}
110
+ spec[:name] = column.name.inspect
111
+
112
+ # AR has an optimisation which handles zero-scale decimals as integers. This
113
+ # code ensures that the dumper still dumps the column as a decimal.
114
+ spec[:type] = if column.type == :integer && [/^numeric/, /^decimal/].any? { |e| e.match(column.sql_type) }
115
+ 'decimal'
116
+ else
117
+ column.type.to_s
118
+ end
119
+ spec[:limit] = column.limit.inspect if column.limit != @types[column.type][:limit] && spec[:type] != 'decimal'
120
+ spec[:precision] = column.precision.inspect if !column.precision.nil?
121
+ spec[:scale] = column.scale.inspect if !column.scale.nil?
122
+ spec[:null] = 'false' if !column.null
123
+ spec[:default] = default_string(column.default) if column.has_default?
124
+
125
+ # Additions for spatial columns
126
+ if column.is_a?(SpatialColumn)
127
+ # Override with specific geometry type
128
+ spec[:type] = column.geometry_type.to_s
129
+ spec[:srid] = column.srid.inspect if column.srid != -1
130
+ spec[:with_z] = 'true' if column.with_z
131
+ spec[:with_m] = 'true' if column.with_m
132
+ end
133
+ spec
134
+ end
135
+ end
@@ -0,0 +1,68 @@
1
+ module SpatialAdapter
2
+ module SpatialColumn
3
+ attr_reader :spatial, :geometry_type, :srid, :with_z, :with_m
4
+
5
+ def initialize(name, default, sql_type = nil, null = true,srid=-1,with_z=false,with_m=false)
6
+ super(name, default, sql_type, null)
7
+ @geometry_type = geometry_simplified_type(@sql_type)
8
+ @srid = srid
9
+ @with_z = with_z
10
+ @with_m = with_m
11
+ end
12
+
13
+ # Redefines type_cast to add support for geometries
14
+ # alias_method :type_cast_without_spatial, :type_cast
15
+ def type_cast(value)
16
+ return nil if value.nil?
17
+ case type
18
+ when :geometry then self.class.string_to_geometry(value)
19
+ else super
20
+ end
21
+ end
22
+
23
+ #Redefines type_cast_code to add support for geometries.
24
+ #
25
+ #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.
26
+ # alias_method :type_cast_code_without_spatial, :type_cast_code
27
+ def type_cast_code(var_name)
28
+ case type
29
+ when :geometry then "#{self.class.name}.string_to_geometry(#{var_name})"
30
+ else super
31
+ end
32
+ end
33
+
34
+
35
+ #Redefines klass to add support for geometries
36
+ # alias_method :klass_without_spatial, :klass
37
+ def klass
38
+ case type
39
+ when :geometry then GeoRuby::SimpleFeatures::Geometry
40
+ else super
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ #Redefines the simplified_type method to add behabiour for when a column is of type geometry
47
+ def simplified_type(field_type)
48
+ case field_type
49
+ when /geometry|point|linestring|polygon|multipoint|multilinestring|multipolygon|geometrycollection/i then :geometry
50
+ else super
51
+ end
52
+ end
53
+
54
+ #less simlpified geometric type to be use in migrations
55
+ def geometry_simplified_type(field_type)
56
+ case field_type
57
+ when /^point$/i then :point
58
+ when /^linestring$/i then :line_string
59
+ when /^polygon$/i then :polygon
60
+ when /^geometry$/i then :geometry
61
+ when /multipoint/i then :multi_point
62
+ when /multilinestring/i then :multi_line_string
63
+ when /multipolygon/i then :multi_polygon
64
+ when /geometrycollection/i then :geometry_collection
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,14 @@
1
+ include SpatialAdapter
2
+
3
+ ActiveRecord::ConnectionAdapters::TableDefinition.class_eval do
4
+ SpatialAdapter.geometry_data_types.keys.each do |column_type|
5
+ class_eval <<-EOV
6
+ def #{column_type}(*args)
7
+ options = args.extract_options!
8
+ column_names = args
9
+
10
+ column_names.each { |name| column(name, '#{column_type}', options) }
11
+ end
12
+ EOV
13
+ end
14
+ end
data/rails/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'spatial_adapter'
data/spec/README.txt ADDED
@@ -0,0 +1,16 @@
1
+ = Running Tests
2
+
3
+ You will need to set up empty databases for each adapter you want to test.
4
+
5
+ == PostgreSQL
6
+
7
+ Create an empty database "spatial_adapter" and ensure that the PostGIS extensions are loaded.
8
+
9
+ run "rake spec:postgis" to run the specs
10
+
11
+ == MySQL
12
+
13
+ Create an empty database "spatial_adapter" - the spatial extensions are already available.
14
+
15
+ run "rake spec:mysql" to run the specs
16
+
@@ -0,0 +1,69 @@
1
+ mysql_connection
2
+
3
+ ActiveRecord::Schema.define() do
4
+ execute "drop table if exists point_models"
5
+ execute "create table point_models
6
+ (
7
+ id int(11) DEFAULT NULL auto_increment PRIMARY KEY,
8
+ extra varchar(255),
9
+ geom point not null
10
+ ) ENGINE=MyISAM"
11
+ execute "create spatial index index_point_models_on_geom on point_models (geom)"
12
+ execute "create index index_point_models_on_extra on point_models (extra)"
13
+
14
+ execute "drop table if exists line_string_models"
15
+ execute "create table line_string_models
16
+ (
17
+ id int(11) DEFAULT NULL auto_increment PRIMARY KEY,
18
+ extra varchar(255),
19
+ geom linestring
20
+ ) ENGINE=MyISAM"
21
+
22
+ execute "drop table if exists polygon_models"
23
+ execute "create table polygon_models
24
+ (
25
+ id int(11) DEFAULT NULL auto_increment PRIMARY KEY,
26
+ extra varchar(255),
27
+ geom polygon
28
+ ) ENGINE=MyISAM"
29
+
30
+ execute "drop table if exists multi_point_models"
31
+ execute "create table multi_point_models
32
+ (
33
+ id int(11) DEFAULT NULL auto_increment PRIMARY KEY,
34
+ extra varchar(255),
35
+ geom multipoint
36
+ ) ENGINE=MyISAM"
37
+
38
+ execute "drop table if exists multi_line_string_models"
39
+ execute "create table multi_line_string_models
40
+ (
41
+ id int(11) DEFAULT NULL auto_increment PRIMARY KEY,
42
+ extra varchar(255),
43
+ geom multilinestring
44
+ ) ENGINE=MyISAM"
45
+
46
+ execute "drop table if exists multi_polygon_models"
47
+ execute "create table multi_polygon_models
48
+ (
49
+ id int(11) DEFAULT NULL auto_increment PRIMARY KEY,
50
+ extra varchar(255),
51
+ geom multipolygon
52
+ ) ENGINE=MyISAM"
53
+
54
+ execute "drop table if exists geometry_collection_models"
55
+ execute "create table geometry_collection_models
56
+ (
57
+ id int(11) DEFAULT NULL auto_increment PRIMARY KEY,
58
+ extra varchar(255),
59
+ geom geometrycollection
60
+ ) ENGINE=MyISAM"
61
+
62
+ execute "drop table if exists geometry_models"
63
+ execute "create table geometry_models
64
+ (
65
+ id int(11) DEFAULT NULL auto_increment PRIMARY KEY,
66
+ extra varchar(255),
67
+ geom geometry
68
+ ) ENGINE=MyISAM"
69
+ end
@@ -0,0 +1,95 @@
1
+ postgis_connection
2
+
3
+ ActiveRecord::Schema.define() do
4
+ execute <<-SQL
5
+ drop table if exists point_models;
6
+ create table point_models
7
+ (
8
+ id serial primary key,
9
+ extra varchar(255)
10
+ );
11
+ select AddGeometryColumn('point_models', 'geom', 4326, 'POINT', 2);
12
+ create index index_point_models_on_geom on point_models using gist (geom);
13
+ create index index_point_models_on_extra on point_models (extra);
14
+
15
+ drop table if exists line_string_models;
16
+ create table line_string_models
17
+ (
18
+ id serial primary key,
19
+ extra varchar(255)
20
+ );
21
+ select AddGeometryColumn('line_string_models', 'geom', 4326, 'LINESTRING', 2);
22
+
23
+ drop table if exists polygon_models;
24
+ create table polygon_models
25
+ (
26
+ id serial primary key,
27
+ extra varchar(255)
28
+ );
29
+ select AddGeometryColumn('polygon_models', 'geom', 4326, 'POLYGON', 2);
30
+
31
+ drop table if exists multi_point_models;
32
+ create table multi_point_models
33
+ (
34
+ id serial primary key,
35
+ extra varchar(255)
36
+ );
37
+ select AddGeometryColumn('multi_point_models', 'geom', 4326, 'MULTIPOINT', 2);
38
+
39
+ drop table if exists multi_line_string_models;
40
+ create table multi_line_string_models
41
+ (
42
+ id serial primary key,
43
+ extra varchar(255)
44
+ );
45
+ select AddGeometryColumn('multi_line_string_models', 'geom', 4326, 'MULTILINESTRING', 2);
46
+
47
+ drop table if exists multi_polygon_models;
48
+ create table multi_polygon_models
49
+ (
50
+ id serial primary key,
51
+ extra varchar(255)
52
+ );
53
+ select AddGeometryColumn('multi_polygon_models', 'geom', 4326, 'MULTIPOLYGON', 2);
54
+
55
+ drop table if exists geometry_collection_models;
56
+ create table geometry_collection_models
57
+ (
58
+ id serial primary key,
59
+ extra varchar(255)
60
+ );
61
+ select AddGeometryColumn('geometry_collection_models', 'geom', 4326, 'GEOMETRYCOLLECTION', 2);
62
+
63
+ drop table if exists geometry_models;
64
+ create table geometry_models
65
+ (
66
+ id serial primary key,
67
+ extra varchar(255)
68
+ );
69
+ select AddGeometryColumn('geometry_models', 'geom', 4326, 'GEOMETRY', 2);
70
+
71
+ drop table if exists pointz_models;
72
+ create table pointz_models
73
+ (
74
+ id serial primary key,
75
+ extra varchar(255)
76
+ );
77
+ select AddGeometryColumn('pointz_models', 'geom', 4326, 'POINT', 3);
78
+
79
+ drop table if exists pointm_models;
80
+ create table pointm_models
81
+ (
82
+ id serial primary key,
83
+ extra varchar(255)
84
+ );
85
+ select AddGeometryColumn('pointm_models', 'geom', 4326, 'POINTM', 3);
86
+
87
+ drop table if exists point4_models;
88
+ create table point4_models
89
+ (
90
+ id serial primary key,
91
+ extra varchar(255)
92
+ );
93
+ select AddGeometryColumn('point4_models', 'geom', 4326, 'POINT', 4);
94
+ SQL
95
+ end
@@ -0,0 +1,32 @@
1
+ class PointModel < ActiveRecord::Base
2
+ end
3
+
4
+ class LineStringModel < ActiveRecord::Base
5
+ end
6
+
7
+ class PolygonModel < ActiveRecord::Base
8
+ end
9
+
10
+ class MultiPointModel < ActiveRecord::Base
11
+ end
12
+
13
+ class MultiLineStringModel < ActiveRecord::Base
14
+ end
15
+
16
+ class MultiPolygonModel < ActiveRecord::Base
17
+ end
18
+
19
+ class GeometryCollectionModel < ActiveRecord::Base
20
+ end
21
+
22
+ class GeometryModel < ActiveRecord::Base
23
+ end
24
+
25
+ class PointzModel < ActiveRecord::Base
26
+ end
27
+
28
+ class PointmModel < ActiveRecord::Base
29
+ end
30
+
31
+ class Point4Model < ActiveRecord::Base
32
+ end