dr-postgis_adapter 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,184 @@
1
+ #
2
+ # PostGIS Adapter
3
+ #
4
+ # Common Spatial Adapter for ActiveRecord
5
+ #
6
+ # Code from
7
+ # http://georuby.rubyforge.org Spatial Adapter
8
+ #
9
+
10
+ #Addition of a flag indicating if the index is spatial
11
+ ActiveRecord::ConnectionAdapters::IndexDefinition.class_eval do
12
+ attr_accessor :spatial
13
+
14
+ def initialize(table, name, unique, spatial,columns)
15
+ super(table,name,unique,columns)
16
+ @spatial = spatial
17
+ end
18
+
19
+ end
20
+
21
+ ActiveRecord::SchemaDumper.class_eval do
22
+ def table(table, stream)
23
+
24
+ columns = @connection.columns(table)
25
+ begin
26
+ tbl = StringIO.new
27
+
28
+ if @connection.respond_to?(:pk_and_sequence_for)
29
+ pk, pk_seq = @connection.pk_and_sequence_for(table)
30
+ end
31
+ pk ||= 'id'
32
+
33
+ tbl.print " create_table #{table.inspect}"
34
+ if columns.detect { |c| c.name == pk }
35
+ if pk != 'id'
36
+ tbl.print %Q(, :primary_key => "#{pk}")
37
+ end
38
+ else
39
+ tbl.print ", :id => false"
40
+ end
41
+
42
+ if @connection.respond_to?(:options_for)
43
+ res = @connection.options_for(table)
44
+ tbl.print ", :options=>'#{res}'" if res
45
+ end
46
+
47
+ tbl.print ", :force => true"
48
+ tbl.puts " do |t|"
49
+
50
+ columns.each do |column|
51
+
52
+ raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}' in table '#{table}'" if @types[column.type].nil?
53
+ next if column.name == pk
54
+ #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
55
+ if column.is_a?(SpatialColumn)
56
+ tbl.print " t.column #{column.name.inspect}, #{column.geometry_type.inspect}"
57
+ tbl.print ", :srid => #{column.srid.inspect}" if column.srid != -1
58
+ tbl.print ", :with_z => #{column.with_z.inspect}" if column.with_z
59
+ tbl.print ", :with_m => #{column.with_m.inspect}" if column.with_m
60
+ else
61
+ tbl.print " t.column #{column.name.inspect}, #{column.type.inspect}"
62
+ end
63
+ tbl.print ", :limit => #{column.limit.inspect}" if column.limit != @types[column.type][:limit] && column.precision.blank? && column.scale.blank?
64
+ tbl.print ", :precision => #{column.precision.inspect}" if column.precision != @types[column.type][:precision]
65
+ tbl.print ", :scale => #{column.scale.inspect}" if column.scale != @types[column.type][:scale]
66
+ tbl.print ", :default => #{default_string(column.default)}" if !column.default.nil?
67
+ tbl.print ", :null => false" if !column.null
68
+ tbl.puts
69
+ end
70
+
71
+ tbl.puts " end"
72
+ tbl.puts
73
+ indexes(table, tbl)
74
+ tbl.rewind
75
+ stream.print tbl.read
76
+ rescue => e
77
+ stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
78
+ stream.puts "# #{e.message} #{e.backtrace}"
79
+ stream.puts
80
+ end
81
+
82
+ stream end
83
+
84
+ def indexes(table, stream)
85
+ indexes = @connection.indexes(table)
86
+ indexes.each do |index|
87
+ stream.print " add_index #{index.table.inspect}, #{index.columns.inspect}, :name => #{index.name.inspect}"
88
+ stream.print ", :unique => true" if index.unique
89
+ stream.print ", :spatial=> true " if index.spatial
90
+ stream.puts
91
+ end
92
+
93
+ stream.puts unless indexes.empty?
94
+ end
95
+ end
96
+
97
+
98
+
99
+
100
+ module SpatialAdapter
101
+ #Translation of geometric data types
102
+ def geometry_data_types
103
+ {
104
+ :point => { :name => "POINT" },
105
+ :line_string => { :name => "LINESTRING" },
106
+ :polygon => { :name => "POLYGON" },
107
+ :geometry_collection => { :name => "GEOMETRYCOLLECTION" },
108
+ :multi_point => { :name => "MULTIPOINT" },
109
+ :multi_line_string => { :name => "MULTILINESTRING" },
110
+ :multi_polygon => { :name => "MULTIPOLYGON" },
111
+ :geometry => { :name => "GEOMETRY"}
112
+ }
113
+ end
114
+
115
+ end
116
+
117
+
118
+ #using a mixin instead of subclassing Column since each adapter can have a specific subclass of Column
119
+ module SpatialColumn
120
+ attr_reader :geometry_type, :srid, :with_z, :with_m
121
+
122
+ def initialize(name, default, sql_type = nil, null = true,srid=-1,with_z=false,with_m=false)
123
+ super(name,default,sql_type,null)
124
+ @geometry_type = geometry_simplified_type(@sql_type)
125
+ @srid = srid
126
+ @with_z = with_z
127
+ @with_m = with_m
128
+ end
129
+
130
+
131
+ #Redefines type_cast to add support for geometries
132
+ def type_cast(value)
133
+ return nil if value.nil?
134
+ case type
135
+ when :geometry then self.class.string_to_geometry(value)
136
+ else super
137
+ end
138
+ end
139
+
140
+ #Redefines type_cast_code to add support for geometries.
141
+ #
142
+ #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.
143
+ def type_cast_code(var_name)
144
+ case type
145
+ when :geometry then "#{self.class.name}.string_to_geometry(#{var_name})"
146
+ else super
147
+ end
148
+ end
149
+
150
+
151
+ #Redefines klass to add support for geometries
152
+ def klass
153
+ case type
154
+ when :geometry then GeoRuby::SimpleFeatures::Geometry
155
+ else super
156
+ end
157
+ end
158
+
159
+ private
160
+
161
+ #Redefines the simplified_type method to add behaviour for when a column is of type geometry
162
+ def simplified_type(field_type)
163
+ case field_type
164
+ when /geometry|point|linestring|polygon|multipoint|multilinestring|multipolygon|geometrycollection/i then :geometry
165
+ else super
166
+ end
167
+ end
168
+
169
+ #less simlpified geometric type to be use in migrations
170
+ def geometry_simplified_type(field_type)
171
+ case field_type
172
+ when /^point$/i then :point
173
+ when /^linestring$/i then :line_string
174
+ when /^polygon$/i then :polygon
175
+ when /^geometry$/i then :geometry
176
+ when /multipoint/i then :multi_point
177
+ when /multilinestring/i then :multi_line_string
178
+ when /multipolygon/i then :multi_polygon
179
+ when /geometrycollection/i then :geometry_collection
180
+ end
181
+ end
182
+
183
+
184
+ end
@@ -0,0 +1,130 @@
1
+ ###
2
+ ##
3
+ #
4
+ # BBox
5
+ #
6
+ #
7
+ module PostgisAdapter
8
+ module Functions
9
+
10
+ #
11
+ # These operators utilize indexes. They compare geometries by bounding boxes.
12
+ #
13
+ # You can use the literal forms or call directly using the 'bbox' method. eg.:
14
+ #
15
+ # @point.bbox(">>", @area)
16
+ # @point.bbox("|&>", @area)
17
+ #
18
+ #
19
+ # Cheatsheet:
20
+ #
21
+ # A &< B => A overlaps or is to the left of B
22
+ # A &> B => A overlaps or is to the right of B
23
+ # A << B => A is strictly to the left of B
24
+ # A >> B => A is strictly to the right of B
25
+ # A &<| B => A overlaps B or is below B
26
+ # A |&> B => A overlaps or is above B
27
+ # A <<| B => A strictly below B
28
+ # A |>> B => A strictly above B
29
+ # A = B => A bbox same as B bbox
30
+ # A @ B => A completely contained by B
31
+ # A ~ B => A completely contains B
32
+ # A && B => A and B bboxes interact
33
+ # A ~= B => A and B geometries are binary equal?
34
+ #
35
+ def bbox(operator, other)
36
+ postgis_calculate(:bbox, [self, other], operator)
37
+ end
38
+
39
+ #
40
+ # bbox literal method.
41
+ #
42
+ def completely_contained_by? other
43
+ bbox("@", other)
44
+ end
45
+
46
+ #
47
+ # bbox literal method.
48
+ #
49
+ def completely_contains? other
50
+ bbox("~", other)
51
+ end
52
+
53
+ #
54
+ # bbox literal method.
55
+ #
56
+ def overlaps_or_above? other
57
+ bbox("|&>", other)
58
+ end
59
+
60
+ #
61
+ # bbox literal method.
62
+ #
63
+ def overlaps_or_below? other
64
+ bbox("&<|", other)
65
+ end
66
+
67
+ #
68
+ # bbox literal method.
69
+ #
70
+ def overlaps_or_left_of? other
71
+ bbox("&<", other)
72
+ end
73
+
74
+ #
75
+ # bbox literal method.
76
+ #
77
+ def overlaps_or_right_of? other
78
+ bbox("&>", other)
79
+ end
80
+
81
+ #
82
+ # bbox literal method.
83
+ #
84
+ def strictly_above? other
85
+ bbox("|>>", other)
86
+ end
87
+
88
+ #
89
+ # bbox literal method.
90
+ #
91
+ def strictly_below? other
92
+ bbox("<<|", other)
93
+ end
94
+
95
+ #
96
+ # bbox literal method.
97
+ #
98
+ def strictly_left_of? other
99
+ bbox("<<", other)
100
+ end
101
+
102
+ #
103
+ # bbox literal method.
104
+ #
105
+ def strictly_right_of? other
106
+ bbox(">>", other)
107
+ end
108
+
109
+ #
110
+ # bbox literal method.
111
+ #
112
+ def interacts_with? other
113
+ bbox("&&", other)
114
+ end
115
+
116
+ #
117
+ # bbox literal method.
118
+ #
119
+ def binary_equal? other
120
+ bbox("~=", other)
121
+ end
122
+
123
+ #
124
+ # bbox literal method.
125
+ #
126
+ def same_as? other
127
+ bbox("=", other)
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,68 @@
1
+ module PostgisAdapter
2
+ module Functions
3
+
4
+ #
5
+ # Class Methods
6
+ #
7
+ module ClassMethods
8
+
9
+ #
10
+ # Returns the closest record
11
+ def closest_to(p, opts = {})
12
+ srid = opts.delete(:srid) || 4326
13
+ opts.merge!(:order => "ST_Distance(#{default_geometry}, GeomFromText('POINT(#{p.x} #{p.y})', #{srid}))")
14
+ find(:first, opts)
15
+ end
16
+
17
+ #
18
+ # Order by distance
19
+ def close_to(p, opts = {})
20
+ srid = opts.delete(:srid) || 4326
21
+ opts.merge!(:order => "ST_Distance(geom, GeomFromText('POINT(#{p.x} #{p.y})', #{srid}))")
22
+ find(:all, opts)
23
+ end
24
+
25
+ def by_length opts = {}
26
+ sort = opts.delete(:sort) || 'asc'
27
+ opts.merge!(:order => "ST_length(geom) #{sort}")
28
+ find(:all, opts)
29
+ end
30
+
31
+ def longest
32
+ find(:first, :order => "ST_length(geom) DESC")
33
+ end
34
+
35
+ def contains(p, srid=4326)
36
+ find(:all, :conditions => ["ST_Contains(geom, GeomFromText('POINT(#{p.x} #{p.y})', #{srid}))"])
37
+ end
38
+
39
+ def contain(p, srid=4326)
40
+ find(:first, :conditions => ["ST_Contains(geom, GeomFromText('POINT(#{p.x} #{p.y})', #{srid}))"])
41
+ end
42
+
43
+ def by_area sort='asc'
44
+ find(:all, :order => "ST_Area(geom) #{sort}" )
45
+ end
46
+
47
+ def by_perimeter sort='asc'
48
+ find(:all, :order => "ST_Perimeter(geom) #{sort}" )
49
+ end
50
+
51
+ def all_dwithin(other, margin=1)
52
+ # find(:all, :conditions => "ST_DWithin(geom, ST_GeomFromEWKB(E'#{other.as_ewkt}'), #{margin})")
53
+ # where "ST_DWithin(ST_GeomFromText(?, 4326), geometry, ?)", location_as_text, distance
54
+ where "ST_DWithin(#{default_geometry}, ST_GeomFromEWKT(?), ?)", other.as_hex_ewkb, margin
55
+ end
56
+
57
+ def all_within(other)
58
+ find(:all, :conditions => "ST_Within(geom, ST_GeomFromEWKT(E'#{other.as_hex_ewkb}'))")
59
+ end
60
+
61
+ def by_boundaries sort='asc'
62
+ find(:all, :order => "ST_Boundary(geom) #{sort}" )
63
+ end
64
+
65
+ end
66
+
67
+ end
68
+ end