activerecord-postgis-adapter 0.2.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/History.rdoc +6 -0
- data/README.rdoc +152 -0
- data/Version +1 -0
- data/lib/active_record/connection_adapters/postgis_adapter.rb +444 -0
- data/test/tc_basic.rb +271 -0
- metadata +100 -0
data/History.rdoc
ADDED
data/README.rdoc
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
== PostGIS \ActiveRecord Adapter
|
2
|
+
|
3
|
+
The PostGIS \ActiveRecord Adapter is an \ActiveRecord connection adapter
|
4
|
+
based on the standard postgresql adapter. It extends the standard adapter
|
5
|
+
to provide support for the spatial extensions provided by PostGIS, using
|
6
|
+
the {RGeo}[http://github.com/dazuma/rgeo] library to represent spatial
|
7
|
+
data in Ruby. Like the standard postgresql adapter, this adapter requires
|
8
|
+
the pg gem.
|
9
|
+
|
10
|
+
=== Usage
|
11
|
+
|
12
|
+
To use this adapter, add this gem, "activerecord-postgis-adapter", to
|
13
|
+
your Gemfile, and then request the adapter name "postgis" in your
|
14
|
+
database connection configuration (which, for a Rails application, is in
|
15
|
+
the config/database.yml file). The other database connection configuration
|
16
|
+
parameters are the same as for the stock postgresql adapter.
|
17
|
+
|
18
|
+
First, this adapter extends the migration syntax to support creating
|
19
|
+
spatial columns and indexes. To create a spatial column, use the
|
20
|
+
<tt>:geometry</tt> type, or any of the OGC spatial types such as
|
21
|
+
<tt>:point</tt> or <tt>:line_string</tt>. To create a geography column,
|
22
|
+
set the <tt>:geographic</tt> option to true when creating the column.
|
23
|
+
To create a spatial index, set the <tt>:spatial</tt> option to true when
|
24
|
+
creating the index.
|
25
|
+
|
26
|
+
Examples:
|
27
|
+
|
28
|
+
create_table :spatial_table do |t|
|
29
|
+
t.column :shape, :geometry # or t.geometry :shape
|
30
|
+
t.line_string :path
|
31
|
+
t.column :latlon, :point, :geographic => true
|
32
|
+
end
|
33
|
+
change_table :spatial_table do |t|
|
34
|
+
t.index :latlon, :spatial => true
|
35
|
+
end
|
36
|
+
|
37
|
+
When this adapter is in use, spatial attributes in your \ActiveRecord
|
38
|
+
objects will have RGeo geometry values. You can set spatial attributes
|
39
|
+
either to RGeo geometry objects, or to strings in WKT (well-known text)
|
40
|
+
format, which the adapter will automatically convert to geometry objects.
|
41
|
+
|
42
|
+
To specify the RGeo geometry factory, you can either set an explicit
|
43
|
+
factory for a column, or provide a factory generator that will yield the
|
44
|
+
appropriate factory for the table's spatial columns based on their types.
|
45
|
+
For the former, call the set_rgeo_factory_for_column class method on your
|
46
|
+
\ActiveRecord class. For the latter, set the rgeo_factory_generator class
|
47
|
+
attribute. This generator should understand the usual <tt>:srid</tt>,
|
48
|
+
<tt>:has_z_coordinate</tt>, and <tt>:has_m_coordinate</tt> options. It
|
49
|
+
will also be passed a <tt>:geographic</tt> option indicating whether the
|
50
|
+
column is a geography column. The set_rgeo_factory_for_column and
|
51
|
+
rgeo_factory_generator methods are actually implemented and documented in
|
52
|
+
the "rgeo-activerecord" gem.
|
53
|
+
|
54
|
+
Examples, given the spatial table defined above:
|
55
|
+
|
56
|
+
class SpatialTable < ActiveRecord::Base
|
57
|
+
|
58
|
+
# By default, use the GEOS implementation for spatial columns.
|
59
|
+
self.rgeo_factory_generator = RGeo::Geos.method(:factory)
|
60
|
+
|
61
|
+
# But use a geographic implementation for the :latlon column.
|
62
|
+
set_rgeo_factory_for_column(:latlon, RGeo::Geographic.spherical_factory)
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
Now you can interact with the data using the RGeo types:
|
67
|
+
|
68
|
+
rec = SpatialTable.new
|
69
|
+
rec.latlon = 'POINT(-122 47)' # You can set by feature object or WKT.
|
70
|
+
loc = rec.latlon # Accessing always returns a feature object, in
|
71
|
+
# this case, a geographic that understands latitude.
|
72
|
+
loc.latitude # => 47
|
73
|
+
rec.shape = loc # the factory for the :shape column is GEOS, so the
|
74
|
+
# value will be cast from geographic to GEOS.
|
75
|
+
RGeo::Geos.is_geos?(rec.shape) # => true
|
76
|
+
|
77
|
+
=== Installation
|
78
|
+
|
79
|
+
This adapter has the following requirements:
|
80
|
+
|
81
|
+
* Ruby 1.8.7 or later. Ruby 1.9.2 or later preferred.
|
82
|
+
* PostGIS 1.5 or later.
|
83
|
+
* \ActiveRecord 3.0.3 or later. Earlier versions will not work.
|
84
|
+
* rgeo gem 0.2.0 or later.
|
85
|
+
* rgeo-activerecord gem 0.2.0 or later.
|
86
|
+
* pg gem 0.10 or later.
|
87
|
+
|
88
|
+
Install this adapter as a gem:
|
89
|
+
|
90
|
+
gem install activerecord-postgis-adapter
|
91
|
+
|
92
|
+
See the README for the "rgeo" gem, a required dependency, for further
|
93
|
+
installation information.
|
94
|
+
|
95
|
+
=== Known bugs and limitations
|
96
|
+
|
97
|
+
A few minor holes still exist in this adapter. Notably, using \ActiveRecord
|
98
|
+
to list indexes doesn't properly identify GiST (spatial) indexes as such.
|
99
|
+
This adapter is also not yet well tested.
|
100
|
+
|
101
|
+
=== Development and support
|
102
|
+
|
103
|
+
Documentation is available at http://virtuoso.rubyforge.org/activerecord-postgis-adapter/README_rdoc.html
|
104
|
+
|
105
|
+
Source code is hosted on Github at http://github.com/dazuma/activerecord-postgis-adapter
|
106
|
+
|
107
|
+
Contributions are welcome. Fork the project on Github.
|
108
|
+
|
109
|
+
Report bugs on Github issues at http://github.org/dazuma/activerecord-postgis-adapter/issues
|
110
|
+
|
111
|
+
Contact the author at dazuma at gmail dot com.
|
112
|
+
|
113
|
+
=== Acknowledgments
|
114
|
+
|
115
|
+
RGeo is written by Daniel Azuma (http://www.daniel-azuma.com).
|
116
|
+
|
117
|
+
Development of RGeo is sponsored by GeoPage, Inc. (http://www.geopage.com).
|
118
|
+
|
119
|
+
This adapter implementation owes some debt to the spatial_adapter plugin
|
120
|
+
(http://github.com/fragility/spatial_adapter). Although we made some
|
121
|
+
different design decisions for this adapter, studying the spatial_adapter
|
122
|
+
source gave us a head start on the implementation.
|
123
|
+
|
124
|
+
=== License
|
125
|
+
|
126
|
+
Copyright 2010 Daniel Azuma
|
127
|
+
|
128
|
+
All rights reserved.
|
129
|
+
|
130
|
+
Redistribution and use in source and binary forms, with or without
|
131
|
+
modification, are permitted provided that the following conditions are met:
|
132
|
+
|
133
|
+
* Redistributions of source code must retain the above copyright notice,
|
134
|
+
this list of conditions and the following disclaimer.
|
135
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
136
|
+
this list of conditions and the following disclaimer in the documentation
|
137
|
+
and/or other materials provided with the distribution.
|
138
|
+
* Neither the name of the copyright holder, nor the names of any other
|
139
|
+
contributors to this software, may be used to endorse or promote products
|
140
|
+
derived from this software without specific prior written permission.
|
141
|
+
|
142
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
143
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
144
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
145
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
146
|
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
147
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
148
|
+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
149
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
150
|
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
151
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
152
|
+
POSSIBILITY OF SUCH DAMAGE.
|
data/Version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.0
|
@@ -0,0 +1,444 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
#
|
3
|
+
# PostGIS adapter for ActiveRecord
|
4
|
+
#
|
5
|
+
# -----------------------------------------------------------------------------
|
6
|
+
# Copyright 2010 Daniel Azuma
|
7
|
+
#
|
8
|
+
# All rights reserved.
|
9
|
+
#
|
10
|
+
# Redistribution and use in source and binary forms, with or without
|
11
|
+
# modification, are permitted provided that the following conditions are met:
|
12
|
+
#
|
13
|
+
# * Redistributions of source code must retain the above copyright notice,
|
14
|
+
# this list of conditions and the following disclaimer.
|
15
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
16
|
+
# this list of conditions and the following disclaimer in the documentation
|
17
|
+
# and/or other materials provided with the distribution.
|
18
|
+
# * Neither the name of the copyright holder, nor the names of any other
|
19
|
+
# contributors to this software, may be used to endorse or promote products
|
20
|
+
# derived from this software without specific prior written permission.
|
21
|
+
#
|
22
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
23
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
24
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
25
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
26
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
27
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
28
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
29
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
30
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
31
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
32
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
33
|
+
# -----------------------------------------------------------------------------
|
34
|
+
;
|
35
|
+
|
36
|
+
|
37
|
+
require 'rgeo/active_record'
|
38
|
+
require 'active_record/connection_adapters/postgresql_adapter'
|
39
|
+
|
40
|
+
|
41
|
+
# :stopdoc:
|
42
|
+
|
43
|
+
module Arel
|
44
|
+
module Visitors
|
45
|
+
VISITORS['postgis'] = ::Arel::Visitors::PostgreSQL
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# :startdoc:
|
50
|
+
|
51
|
+
|
52
|
+
# The activerecord-postgis-adapter gem installs the *postgis*
|
53
|
+
# connection adapter into ActiveRecord.
|
54
|
+
|
55
|
+
module ActiveRecord
|
56
|
+
|
57
|
+
|
58
|
+
# ActiveRecord looks for the postgis_connection factory method in
|
59
|
+
# this class.
|
60
|
+
|
61
|
+
class Base
|
62
|
+
|
63
|
+
|
64
|
+
# Create a postgis connection adapter.
|
65
|
+
|
66
|
+
def self.postgis_connection(config_)
|
67
|
+
require 'pg'
|
68
|
+
|
69
|
+
config_ = config_.symbolize_keys
|
70
|
+
host_ = config_[:host]
|
71
|
+
port_ = config_[:port] || 5432
|
72
|
+
username_ = config_[:username].to_s if config_[:username]
|
73
|
+
password_ = config_[:password].to_s if config_[:password]
|
74
|
+
if config_.has_key?(:database)
|
75
|
+
database_ = config_[:database]
|
76
|
+
else
|
77
|
+
raise ::ArgumentError, "No database specified. Missing argument: database."
|
78
|
+
end
|
79
|
+
|
80
|
+
# The postgres drivers don't allow the creation of an unconnected PGconn object,
|
81
|
+
# so just pass a nil connection object for the time being.
|
82
|
+
ConnectionAdapters::PostGisAdapter.new(nil, logger, [host_, port_, nil, nil, database_, username_, password_], config_)
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
module ConnectionAdapters # :nodoc:
|
90
|
+
|
91
|
+
|
92
|
+
class PostGisAdapter < PostgreSQLAdapter # :nodoc:
|
93
|
+
|
94
|
+
|
95
|
+
ADAPTER_NAME = 'PostGIS'.freeze
|
96
|
+
|
97
|
+
@@native_database_types = nil
|
98
|
+
|
99
|
+
|
100
|
+
def native_database_types
|
101
|
+
@@native_database_types ||= super.merge(:geometry => {:name => "geometry"}, :point => {:name => "point"}, :line_string => {:name => "linestring"}, :polygon => {:name => "polygon"}, :geometry_collection => {:name => "geometrycollection"}, :multi_point => {:name => "multipoint"}, :multi_line_string => {:name => "multilinestring"}, :multi_polygon => {:name => "multipolygon"})
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
def adapter_name
|
106
|
+
ADAPTER_NAME
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
def postgis_lib_version
|
111
|
+
unless defined?(@postgis_lib_version)
|
112
|
+
@postgis_lib_version = select_value("SELECT PostGIS_Lib_Version()") rescue nil
|
113
|
+
end
|
114
|
+
@postgis_lib_version
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
def quote(value_, column_=nil)
|
119
|
+
if ::RGeo::Feature::Geometry.check_type(value_)
|
120
|
+
"'#{::RGeo::WKRep::WKBGenerator.new(:hex_format => true, :type_format => :ewkb, :emit_ewkb_srid => true).generate(value_)}'"
|
121
|
+
else
|
122
|
+
super
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
def columns(table_name_, name_=nil) #:nodoc:
|
128
|
+
table_name_ = table_name_.to_s
|
129
|
+
spatial_info_ = spatial_column_info(table_name_)
|
130
|
+
column_definitions(table_name_).collect do |name_, type_, default_, notnull_|
|
131
|
+
SpatialColumn.new(name_, default_, type_, notnull_ == 'f', type_ =~ /geometry/i ? spatial_info_[name_] : nil)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
def create_table(table_name_, options_={})
|
137
|
+
table_name_ = table_name_.to_s
|
138
|
+
table_definition_ = SpatialTableDefinition.new(self)
|
139
|
+
table_definition_.primary_key(options_[:primary_key] || ::ActiveRecord::Base.get_primary_key(table_name_.singularize)) unless options_[:id] == false
|
140
|
+
yield table_definition_ if block_given?
|
141
|
+
if options_[:force] && table_exists?(table_name_)
|
142
|
+
drop_table(table_name_, options_)
|
143
|
+
end
|
144
|
+
|
145
|
+
create_sql_ = "CREATE#{' TEMPORARY' if options_[:temporary]} TABLE "
|
146
|
+
create_sql_ << "#{quote_table_name(table_name_)} ("
|
147
|
+
create_sql_ << table_definition_.to_sql
|
148
|
+
create_sql_ << ") #{options_[:options]}"
|
149
|
+
execute create_sql_
|
150
|
+
|
151
|
+
table_definition_.non_geographic_spatial_columns.each do |col_|
|
152
|
+
type_ = col_.type.to_s.gsub('_', '').upcase
|
153
|
+
has_z_ = col_.has_z?
|
154
|
+
has_m_ = col_.has_m?
|
155
|
+
type_ = "#{type_}M" if has_m_ && !has_z_
|
156
|
+
dimensions_ = 2
|
157
|
+
dimensions_ += 1 if has_z_
|
158
|
+
dimensions_ += 1 if has_m_
|
159
|
+
execute("SELECT AddGeometryColumn('#{quote_string(table_name_)}', '#{quote_string(col_.name)}', #{col_.srid}, '#{quote_string(type_)}', #{dimensions_})")
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
def drop_table(table_name_, options_={})
|
165
|
+
execute("DELETE from geometry_columns where f_table_name='#{quote_string(table_name_.to_s)}'")
|
166
|
+
super
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
def add_column(table_name_, column_name_, type_, options_={})
|
171
|
+
table_name_ = table_name_.to_s
|
172
|
+
if ::RGeo::ActiveRecord::GEOMETRY_TYPES.include?(type_.to_sym)
|
173
|
+
type_ = type_.to_s.gsub('_', '').upcase
|
174
|
+
has_z_ = options_[:has_z]
|
175
|
+
has_m_ = options_[:has_m]
|
176
|
+
srid_ = (options_[:srid] || 4326).to_i
|
177
|
+
if options_[:geographic]
|
178
|
+
type_ << 'Z' if has_z_
|
179
|
+
type_ << 'M' if has_m_
|
180
|
+
execute("ALTER TABLE #{quote_table_name(table_name_)} ADD COLUMN #{quote_column_name(column_name_)} GEOGRAPHY(#{type_},#{srid_})")
|
181
|
+
change_column_default(table_name_, column_name_, options_[:default]) if options_include_default?(options_)
|
182
|
+
change_column_null(table_name_, column_name_, false, options_[:default]) if options_[:null] == false
|
183
|
+
else
|
184
|
+
type_ = "#{type_}M" if has_m_ && !has_z_
|
185
|
+
dimensions_ = 2
|
186
|
+
dimensions_ += 1 if has_z_
|
187
|
+
dimensions_ += 1 if has_m_
|
188
|
+
execute("SELECT AddGeometryColumn('#{quote_string(table_name_)}', '#{quote_string(column_name_.to_s)}', #{srid_}, '#{quote_string(type_)}', #{dimensions_})")
|
189
|
+
end
|
190
|
+
else
|
191
|
+
super
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
|
196
|
+
def remove_column(table_name_, *column_names_)
|
197
|
+
column_names_ = column_names_.flatten.map{ |n_| n_.to_s }
|
198
|
+
spatial_info_ = spatial_column_info(table_name_)
|
199
|
+
remaining_column_names_ = []
|
200
|
+
column_names_.each do |name_|
|
201
|
+
if spatial_info_.include?(name_)
|
202
|
+
execute("SELECT DropGeometryColumn('#{quote_string(table_name_.to_s)}','#{quote_string(name_)}')")
|
203
|
+
else
|
204
|
+
remaining_column_names_ << name_.to_sym
|
205
|
+
end
|
206
|
+
end
|
207
|
+
if remaining_column_names_.size > 0
|
208
|
+
super(table_name_, *remaining_column_names_)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
|
213
|
+
def add_index(table_name_, column_name_, options_={})
|
214
|
+
table_name_ = table_name_.to_s
|
215
|
+
column_names_ = ::Array.wrap(column_name_)
|
216
|
+
index_name_ = index_name(table_name_, :column => column_names_)
|
217
|
+
gist_clause_ = ''
|
218
|
+
index_type_ = ''
|
219
|
+
if ::Hash === options_ # legacy support, since this param was a string
|
220
|
+
index_type_ = 'UNIQUE' if options_[:unique]
|
221
|
+
index_name_ = options_[:name].to_s if options_.key?(:name)
|
222
|
+
gist_clause_ = 'USING GIST' if options_[:spatial]
|
223
|
+
else
|
224
|
+
index_type_ = options_
|
225
|
+
end
|
226
|
+
if index_name_.length > index_name_length
|
227
|
+
raise ::ArgumentError, "Index name '#{index_name_}' on table '#{table_name_}' is too long; the limit is #{index_name_length} characters"
|
228
|
+
end
|
229
|
+
if index_name_exists?(table_name_, index_name_, false)
|
230
|
+
raise ::ArgumentError, "Index name '#{index_name_}' on table '#{table_name_}' already exists"
|
231
|
+
end
|
232
|
+
quoted_column_names_ = quoted_columns_for_index(column_names_, options_).join(", ")
|
233
|
+
execute "CREATE #{index_type_} INDEX #{quote_column_name(index_name_)} ON #{quote_table_name(table_name_)} #{gist_clause_} (#{quoted_column_names_})"
|
234
|
+
end
|
235
|
+
|
236
|
+
|
237
|
+
def spatial_column_info(table_name_)
|
238
|
+
info_ = query("SELECT * FROM geometry_columns WHERE f_table_name='#{quote_string(table_name_.to_s)}'")
|
239
|
+
result_ = {}
|
240
|
+
info_.each do |row_|
|
241
|
+
name_ = row_[3]
|
242
|
+
type_ = row_[6]
|
243
|
+
dimension_ = row_[4].to_i
|
244
|
+
has_m_ = type_ =~ /m$/i ? true : false
|
245
|
+
type_.sub!(/m$/, '')
|
246
|
+
has_z_ = dimension_ > 3 || dimension_ == 3 && !has_m_
|
247
|
+
result_[name_] = {
|
248
|
+
:name => name_,
|
249
|
+
:type => type_,
|
250
|
+
:dimension => dimension_,
|
251
|
+
:srid => row_[5].to_i,
|
252
|
+
:has_z => has_z_,
|
253
|
+
:has_m => has_m_,
|
254
|
+
}
|
255
|
+
end
|
256
|
+
result_
|
257
|
+
end
|
258
|
+
|
259
|
+
|
260
|
+
class SpatialTableDefinition < ConnectionAdapters::TableDefinition # :nodoc:
|
261
|
+
|
262
|
+
attr_reader :spatial_columns
|
263
|
+
|
264
|
+
def initialize(base_)
|
265
|
+
super
|
266
|
+
end
|
267
|
+
|
268
|
+
def column(name_, type_, options_={})
|
269
|
+
super
|
270
|
+
col_ = self[name_]
|
271
|
+
if ::RGeo::ActiveRecord::GEOMETRY_TYPES.include?(col_.type.to_sym)
|
272
|
+
col_.extend(GeometricColumnDefinitionMethods) unless col_.respond_to?(:geographic?)
|
273
|
+
col_.set_geographic(options_[:geographic])
|
274
|
+
col_.set_srid((options_[:srid] || 4326).to_i)
|
275
|
+
col_.set_has_z(options_[:has_z])
|
276
|
+
col_.set_has_m(options_[:has_m])
|
277
|
+
end
|
278
|
+
self
|
279
|
+
end
|
280
|
+
|
281
|
+
def to_sql
|
282
|
+
@columns.find_all{ |c_| !c_.respond_to?(:geographic?) || c_.geographic? }.map{ |c_| c_.to_sql } * ', '
|
283
|
+
end
|
284
|
+
|
285
|
+
def non_geographic_spatial_columns
|
286
|
+
@columns.find_all{ |c_| c_.respond_to?(:geographic?) && !c_.geographic? }
|
287
|
+
end
|
288
|
+
|
289
|
+
end
|
290
|
+
|
291
|
+
|
292
|
+
module GeometricColumnDefinitionMethods # :nodoc:
|
293
|
+
|
294
|
+
def geographic?
|
295
|
+
defined?(@geographic) && @geographic
|
296
|
+
end
|
297
|
+
|
298
|
+
def srid
|
299
|
+
defined?(@srid) ? @srid : 4326
|
300
|
+
end
|
301
|
+
|
302
|
+
def has_z?
|
303
|
+
defined?(@has_z) && @has_z
|
304
|
+
end
|
305
|
+
|
306
|
+
def has_m?
|
307
|
+
defined?(@has_m) && @has_m
|
308
|
+
end
|
309
|
+
|
310
|
+
def set_geographic(value_)
|
311
|
+
@geographic = value_ ? true : false
|
312
|
+
end
|
313
|
+
|
314
|
+
def set_srid(value_)
|
315
|
+
@srid = value_
|
316
|
+
end
|
317
|
+
|
318
|
+
def set_has_z(value_)
|
319
|
+
@has_z = value_ ? true : false
|
320
|
+
end
|
321
|
+
|
322
|
+
def set_has_m(value_)
|
323
|
+
@has_m = value_ ? true : false
|
324
|
+
end
|
325
|
+
|
326
|
+
def sql_type
|
327
|
+
type_ = type.to_s.upcase.gsub('_', '')
|
328
|
+
type_ << 'Z' if has_z?
|
329
|
+
type_ << 'M' if has_m?
|
330
|
+
"GEOGRAPHY(#{type_},#{srid})"
|
331
|
+
end
|
332
|
+
|
333
|
+
end
|
334
|
+
|
335
|
+
|
336
|
+
class SpatialColumn < ConnectionAdapters::PostgreSQLColumn # :nodoc:
|
337
|
+
|
338
|
+
|
339
|
+
def initialize(name_, default_, sql_type_=nil, null_=true, opts_=nil)
|
340
|
+
super(name_, default_, sql_type_, null_)
|
341
|
+
@geographic = sql_type_ =~ /^geography/ ? true : false
|
342
|
+
if opts_
|
343
|
+
@geometric_type = ::RGeo::ActiveRecord.geometric_type_from_name(opts_[:type])
|
344
|
+
@srid = opts_[:srid].to_i
|
345
|
+
@has_z = opts_[:has_z]
|
346
|
+
@has_m = opts_[:has_m]
|
347
|
+
elsif @geographic
|
348
|
+
if sql_type_ =~ /geography\((\w+[^,zm])(z?)(m?),(\d+)\)/i
|
349
|
+
@has_z = $2.length > 0
|
350
|
+
@has_m = $3.length > 0
|
351
|
+
@srid = $4.to_i
|
352
|
+
@geometric_type = ::RGeo::ActiveRecord.geometric_type_from_name($1)
|
353
|
+
else
|
354
|
+
@geometric_type = ::RGeo::Feature::Geometry
|
355
|
+
@srid = 4326
|
356
|
+
@has_z = @has_m = false
|
357
|
+
end
|
358
|
+
else
|
359
|
+
@geometric_type = @has_z = @has_m = nil
|
360
|
+
@srid = 0
|
361
|
+
end
|
362
|
+
@ar_class = ::ActiveRecord::Base
|
363
|
+
end
|
364
|
+
|
365
|
+
|
366
|
+
def set_ar_class(val_)
|
367
|
+
@ar_class = val_
|
368
|
+
end
|
369
|
+
|
370
|
+
|
371
|
+
attr_reader :srid
|
372
|
+
attr_reader :geometric_type
|
373
|
+
attr_reader :has_z
|
374
|
+
attr_reader :has_m
|
375
|
+
|
376
|
+
|
377
|
+
def spatial?
|
378
|
+
type == :geometry
|
379
|
+
end
|
380
|
+
|
381
|
+
|
382
|
+
def geographic?
|
383
|
+
@geographic
|
384
|
+
end
|
385
|
+
|
386
|
+
|
387
|
+
def klass
|
388
|
+
type == :geometry ? ::RGeo::Feature::Geometry : super
|
389
|
+
end
|
390
|
+
|
391
|
+
|
392
|
+
def type_cast(value_)
|
393
|
+
type == :geometry ? SpatialColumn.convert_to_geometry(value_, @ar_class, name, @geographic, @srid, @has_z, @has_m) : super
|
394
|
+
end
|
395
|
+
|
396
|
+
|
397
|
+
def type_cast_code(var_name_)
|
398
|
+
type == :geometry ? "::ActiveRecord::ConnectionAdapters::PostGisAdapter::SpatialColumn.convert_to_geometry(#{var_name_}, self.class, #{name.inspect}, #{@geographic ? 'true' : 'false'}, #{@srid.inspect}, #{@has_z ? 'true' : 'false'}, #{@has_m ? 'true' : 'false'})" : super
|
399
|
+
end
|
400
|
+
|
401
|
+
|
402
|
+
private
|
403
|
+
|
404
|
+
|
405
|
+
def simplified_type(sql_type_)
|
406
|
+
sql_type_ =~ /geography|geometry|point|linestring|polygon/i ? :geometry : super
|
407
|
+
end
|
408
|
+
|
409
|
+
|
410
|
+
def self.convert_to_geometry(input_, ar_class_, column_, geographic_, srid_, has_z_, has_m_)
|
411
|
+
case input_
|
412
|
+
when ::RGeo::Feature::Geometry
|
413
|
+
factory_ = ar_class_.rgeo_factory_for_column(column_, :srid => srid_, :has_z_coordinate => has_z_, :has_m_coordinate => has_m_, :geographic => geographic_)
|
414
|
+
::RGeo::Feature.cast(input_, factory_)
|
415
|
+
when ::String
|
416
|
+
if input_.length == 0
|
417
|
+
nil
|
418
|
+
else
|
419
|
+
factory_ = ar_class_.rgeo_factory_for_column(column_, :srid => srid_, :has_z_coordinate => has_z_, :has_m_coordinate => has_m_, :geographic => geographic_)
|
420
|
+
marker_ = input_[0,1]
|
421
|
+
if marker_ == "\x00" || marker_ == "\x01"
|
422
|
+
::RGeo::WKRep::WKBParser.new(factory_, :support_ewkb => true).parse(input_) rescue nil
|
423
|
+
elsif input_[0,4] =~ /[0-9a-fA-F]{4}/
|
424
|
+
::RGeo::WKRep::WKBParser.new(factory_, :support_ewkb => true).parse_hex(input_) rescue nil
|
425
|
+
else
|
426
|
+
::RGeo::WKRep::WKTParser.new(factory_, :support_ewkt => true).parse(input_) rescue nil
|
427
|
+
end
|
428
|
+
end
|
429
|
+
else
|
430
|
+
nil
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
|
435
|
+
end
|
436
|
+
|
437
|
+
|
438
|
+
end
|
439
|
+
|
440
|
+
|
441
|
+
end
|
442
|
+
|
443
|
+
|
444
|
+
end
|
data/test/tc_basic.rb
ADDED
@@ -0,0 +1,271 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
#
|
3
|
+
# Tests for the PostGIS ActiveRecord adapter
|
4
|
+
#
|
5
|
+
# -----------------------------------------------------------------------------
|
6
|
+
# Copyright 2010 Daniel Azuma
|
7
|
+
#
|
8
|
+
# All rights reserved.
|
9
|
+
#
|
10
|
+
# Redistribution and use in source and binary forms, with or without
|
11
|
+
# modification, are permitted provided that the following conditions are met:
|
12
|
+
#
|
13
|
+
# * Redistributions of source code must retain the above copyright notice,
|
14
|
+
# this list of conditions and the following disclaimer.
|
15
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
16
|
+
# this list of conditions and the following disclaimer in the documentation
|
17
|
+
# and/or other materials provided with the distribution.
|
18
|
+
# * Neither the name of the copyright holder, nor the names of any other
|
19
|
+
# contributors to this software, may be used to endorse or promote products
|
20
|
+
# derived from this software without specific prior written permission.
|
21
|
+
#
|
22
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
23
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
24
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
25
|
+
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
26
|
+
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
27
|
+
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
28
|
+
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
29
|
+
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
30
|
+
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
31
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
32
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
33
|
+
# -----------------------------------------------------------------------------
|
34
|
+
;
|
35
|
+
|
36
|
+
require 'test/unit'
|
37
|
+
require 'rgeo/active_record/adapter_test_helper'
|
38
|
+
|
39
|
+
|
40
|
+
module RGeo
|
41
|
+
module ActiveRecord # :nodoc:
|
42
|
+
module PostGISAdapter # :nodoc:
|
43
|
+
module Tests # :nodoc:
|
44
|
+
|
45
|
+
class TestBasic < ::Test::Unit::TestCase # :nodoc:
|
46
|
+
|
47
|
+
DATABASE_CONFIG_PATH = ::File.dirname(__FILE__)+'/database.yml'
|
48
|
+
include AdapterTestHelper
|
49
|
+
|
50
|
+
define_test_methods do
|
51
|
+
|
52
|
+
|
53
|
+
def populate_ar_class(content_)
|
54
|
+
klass_ = create_ar_class
|
55
|
+
case content_
|
56
|
+
when :latlon_point
|
57
|
+
klass_.connection.create_table(:spatial_test) do |t_|
|
58
|
+
t_.column 'latlon', :point, :srid => 4326
|
59
|
+
end
|
60
|
+
when :latlon_point_geographic
|
61
|
+
klass_.connection.create_table(:spatial_test) do |t_|
|
62
|
+
t_.column 'latlon', :point, :srid => 4326, :geographic => true
|
63
|
+
end
|
64
|
+
end
|
65
|
+
klass_
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
def test_postgis_available
|
70
|
+
connection_ = create_ar_class.connection
|
71
|
+
assert_equal('PostGIS', connection_.adapter_name)
|
72
|
+
assert_not_nil(connection_.postgis_lib_version)
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
def test_create_simple_geometry
|
77
|
+
klass_ = create_ar_class
|
78
|
+
klass_.connection.create_table(:spatial_test) do |t_|
|
79
|
+
t_.column 'latlon', :geometry
|
80
|
+
end
|
81
|
+
assert_equal(1, klass_.connection.select_value("SELECT COUNT(*) FROM geometry_columns WHERE f_table_name='spatial_test'").to_i)
|
82
|
+
col_ = klass_.columns.last
|
83
|
+
assert_equal(::RGeo::Feature::Geometry, col_.geometric_type)
|
84
|
+
assert_equal(false, col_.geographic?)
|
85
|
+
assert_equal(4326, col_.srid)
|
86
|
+
assert(klass_.cached_attributes.include?('latlon'))
|
87
|
+
klass_.connection.drop_table(:spatial_test)
|
88
|
+
assert_equal(0, klass_.connection.select_value("SELECT COUNT(*) FROM geometry_columns WHERE f_table_name='spatial_test'").to_i)
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
def test_create_simple_geography
|
93
|
+
klass_ = create_ar_class
|
94
|
+
klass_.connection.create_table(:spatial_test) do |t_|
|
95
|
+
t_.column 'latlon', :geometry, :geographic => true
|
96
|
+
end
|
97
|
+
col_ = klass_.columns.last
|
98
|
+
assert_equal(::RGeo::Feature::Geometry, col_.geometric_type)
|
99
|
+
assert_equal(true, col_.geographic?)
|
100
|
+
assert_equal(4326, col_.srid)
|
101
|
+
assert(klass_.cached_attributes.include?('latlon'))
|
102
|
+
assert_equal(0, klass_.connection.select_value("SELECT COUNT(*) FROM geometry_columns WHERE f_table_name='spatial_test'").to_i)
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
def test_create_point_geometry
|
107
|
+
klass_ = create_ar_class
|
108
|
+
klass_.connection.create_table(:spatial_test) do |t_|
|
109
|
+
t_.column 'latlon', :point
|
110
|
+
end
|
111
|
+
assert_equal(::RGeo::Feature::Point, klass_.columns.last.geometric_type)
|
112
|
+
assert(klass_.cached_attributes.include?('latlon'))
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
def test_create_geometry_with_index
|
117
|
+
klass_ = create_ar_class
|
118
|
+
klass_.connection.create_table(:spatial_test) do |t_|
|
119
|
+
t_.column 'latlon', :geometry
|
120
|
+
end
|
121
|
+
klass_.connection.change_table(:spatial_test) do |t_|
|
122
|
+
t_.index([:latlon], :spatial => true)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
def test_set_and_get_point
|
128
|
+
klass_ = populate_ar_class(:latlon_point)
|
129
|
+
obj_ = klass_.new
|
130
|
+
assert_nil(obj_.latlon)
|
131
|
+
obj_.latlon = @factory.point(1, 2)
|
132
|
+
assert_equal(@factory.point(1, 2), obj_.latlon)
|
133
|
+
assert_equal(4326, obj_.latlon.srid)
|
134
|
+
end
|
135
|
+
|
136
|
+
|
137
|
+
def test_set_and_get_point_from_wkt
|
138
|
+
klass_ = populate_ar_class(:latlon_point)
|
139
|
+
obj_ = klass_.new
|
140
|
+
assert_nil(obj_.latlon)
|
141
|
+
obj_.latlon = 'POINT(1 2)'
|
142
|
+
assert_equal(@factory.point(1, 2), obj_.latlon)
|
143
|
+
assert_equal(4326, obj_.latlon.srid)
|
144
|
+
end
|
145
|
+
|
146
|
+
|
147
|
+
def test_save_and_load_point
|
148
|
+
klass_ = populate_ar_class(:latlon_point)
|
149
|
+
obj_ = klass_.new
|
150
|
+
obj_.latlon = @factory.point(1, 2)
|
151
|
+
obj_.save!
|
152
|
+
id_ = obj_.id
|
153
|
+
obj2_ = klass_.find(id_)
|
154
|
+
assert_equal(@factory.point(1, 2), obj2_.latlon)
|
155
|
+
assert_equal(4326, obj2_.latlon.srid)
|
156
|
+
assert_equal(true, ::RGeo::Geos.is_geos?(obj2_.latlon))
|
157
|
+
end
|
158
|
+
|
159
|
+
|
160
|
+
def test_save_and_load_geographic_point
|
161
|
+
klass_ = populate_ar_class(:latlon_point_geographic)
|
162
|
+
obj_ = klass_.new
|
163
|
+
obj_.latlon = @factory.point(1, 2)
|
164
|
+
obj_.save!
|
165
|
+
id_ = obj_.id
|
166
|
+
obj2_ = klass_.find(id_)
|
167
|
+
assert_equal(@geographic_factory.point(1, 2), obj2_.latlon)
|
168
|
+
assert_equal(4326, obj2_.latlon.srid)
|
169
|
+
assert_equal(false, ::RGeo::Geos.is_geos?(obj2_.latlon))
|
170
|
+
end
|
171
|
+
|
172
|
+
|
173
|
+
def test_save_and_load_point_from_wkt
|
174
|
+
klass_ = populate_ar_class(:latlon_point)
|
175
|
+
obj_ = klass_.new
|
176
|
+
obj_.latlon = 'POINT(1 2)'
|
177
|
+
obj_.save!
|
178
|
+
id_ = obj_.id
|
179
|
+
obj2_ = klass_.find(id_)
|
180
|
+
assert_equal(@factory.point(1, 2), obj2_.latlon)
|
181
|
+
assert_equal(4326, obj2_.latlon.srid)
|
182
|
+
end
|
183
|
+
|
184
|
+
|
185
|
+
def test_add_geometry_column
|
186
|
+
klass_ = create_ar_class
|
187
|
+
klass_.connection.create_table(:spatial_test) do |t_|
|
188
|
+
t_.column('latlon', :geometry)
|
189
|
+
end
|
190
|
+
klass_.connection.change_table(:spatial_test) do |t_|
|
191
|
+
t_.column('geom2', :point, :srid => 4326)
|
192
|
+
t_.column('name', :string)
|
193
|
+
end
|
194
|
+
assert_equal(2, klass_.connection.select_value("SELECT COUNT(*) FROM geometry_columns WHERE f_table_name='spatial_test'").to_i)
|
195
|
+
cols_ = klass_.columns
|
196
|
+
assert_equal(::RGeo::Feature::Geometry, cols_[-3].geometric_type)
|
197
|
+
assert_equal(4326, cols_[-3].srid)
|
198
|
+
assert_equal(::RGeo::Feature::Point, cols_[-2].geometric_type)
|
199
|
+
assert_equal(4326, cols_[-2].srid)
|
200
|
+
assert_equal(false, cols_[-2].geographic?)
|
201
|
+
assert_nil(cols_[-1].geometric_type)
|
202
|
+
end
|
203
|
+
|
204
|
+
|
205
|
+
def test_add_geography_column
|
206
|
+
klass_ = create_ar_class
|
207
|
+
klass_.connection.create_table(:spatial_test) do |t_|
|
208
|
+
t_.column('latlon', :geometry)
|
209
|
+
end
|
210
|
+
klass_.connection.change_table(:spatial_test) do |t_|
|
211
|
+
t_.column('geom2', :point, :srid => 4326, :geographic => true)
|
212
|
+
t_.column('name', :string)
|
213
|
+
end
|
214
|
+
assert_equal(1, klass_.connection.select_value("SELECT COUNT(*) FROM geometry_columns WHERE f_table_name='spatial_test'").to_i)
|
215
|
+
cols_ = klass_.columns
|
216
|
+
assert_equal(::RGeo::Feature::Geometry, cols_[-3].geometric_type)
|
217
|
+
assert_equal(4326, cols_[-3].srid)
|
218
|
+
assert_equal(::RGeo::Feature::Point, cols_[-2].geometric_type)
|
219
|
+
assert_equal(4326, cols_[-2].srid)
|
220
|
+
assert_equal(true, cols_[-2].geographic?)
|
221
|
+
assert_nil(cols_[-1].geometric_type)
|
222
|
+
end
|
223
|
+
|
224
|
+
|
225
|
+
def test_drop_geometry_column
|
226
|
+
klass_ = create_ar_class
|
227
|
+
klass_.connection.create_table(:spatial_test) do |t_|
|
228
|
+
t_.column('latlon', :geometry)
|
229
|
+
t_.column('geom2', :point, :srid => 4326)
|
230
|
+
end
|
231
|
+
klass_.connection.change_table(:spatial_test) do |t_|
|
232
|
+
t_.remove('geom2')
|
233
|
+
end
|
234
|
+
assert_equal(1, klass_.connection.select_value("SELECT COUNT(*) FROM geometry_columns WHERE f_table_name='spatial_test'").to_i)
|
235
|
+
cols_ = klass_.columns
|
236
|
+
assert_equal(::RGeo::Feature::Geometry, cols_[-1].geometric_type)
|
237
|
+
assert_equal('latlon', cols_[-1].name)
|
238
|
+
assert_equal(4326, cols_[-1].srid)
|
239
|
+
assert_equal(false, cols_[-1].geographic?)
|
240
|
+
end
|
241
|
+
|
242
|
+
|
243
|
+
def test_drop_geography_column
|
244
|
+
klass_ = create_ar_class
|
245
|
+
klass_.connection.create_table(:spatial_test) do |t_|
|
246
|
+
t_.column('latlon', :geometry)
|
247
|
+
t_.column('geom2', :point, :srid => 4326, :geographic => true)
|
248
|
+
t_.column('geom3', :point, :srid => 4326)
|
249
|
+
end
|
250
|
+
klass_.connection.change_table(:spatial_test) do |t_|
|
251
|
+
t_.remove('geom2')
|
252
|
+
end
|
253
|
+
assert_equal(2, klass_.connection.select_value("SELECT COUNT(*) FROM geometry_columns WHERE f_table_name='spatial_test'").to_i)
|
254
|
+
cols_ = klass_.columns
|
255
|
+
assert_equal(::RGeo::Feature::Point, cols_[-1].geometric_type)
|
256
|
+
assert_equal('geom3', cols_[-1].name)
|
257
|
+
assert_equal(false, cols_[-1].geographic?)
|
258
|
+
assert_equal(::RGeo::Feature::Geometry, cols_[-2].geometric_type)
|
259
|
+
assert_equal('latlon', cols_[-2].name)
|
260
|
+
assert_equal(false, cols_[-2].geographic?)
|
261
|
+
end
|
262
|
+
|
263
|
+
|
264
|
+
end
|
265
|
+
|
266
|
+
end
|
267
|
+
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
metadata
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: activerecord-postgis-adapter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Daniel Azuma
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-12-07 00:00:00 -08:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rgeo-activerecord
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
- 2
|
31
|
+
- 0
|
32
|
+
version: 0.2.0
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: pg
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
segments:
|
44
|
+
- 0
|
45
|
+
- 10
|
46
|
+
- 0
|
47
|
+
version: 0.10.0
|
48
|
+
type: :runtime
|
49
|
+
version_requirements: *id002
|
50
|
+
description: This is an ActiveRecord connection adapter for PostGIS. It is based on the stock PostgreSQL adapter, but provides built-in support for the spatial extensions provided by PostGIS. It uses the RGeo library to represent spatial data in Ruby.
|
51
|
+
email: dazuma@gmail.com
|
52
|
+
executables: []
|
53
|
+
|
54
|
+
extensions: []
|
55
|
+
|
56
|
+
extra_rdoc_files:
|
57
|
+
- History.rdoc
|
58
|
+
- README.rdoc
|
59
|
+
files:
|
60
|
+
- lib/active_record/connection_adapters/postgis_adapter.rb
|
61
|
+
- History.rdoc
|
62
|
+
- README.rdoc
|
63
|
+
- test/tc_basic.rb
|
64
|
+
- Version
|
65
|
+
has_rdoc: true
|
66
|
+
homepage: http://virtuoso.rubyforge.org/activerecord-postgis-adapter
|
67
|
+
licenses: []
|
68
|
+
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options: []
|
71
|
+
|
72
|
+
require_paths:
|
73
|
+
- lib
|
74
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
segments:
|
80
|
+
- 1
|
81
|
+
- 8
|
82
|
+
- 7
|
83
|
+
version: 1.8.7
|
84
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
segments:
|
90
|
+
- 0
|
91
|
+
version: "0"
|
92
|
+
requirements: []
|
93
|
+
|
94
|
+
rubyforge_project: virtuoso
|
95
|
+
rubygems_version: 1.3.7
|
96
|
+
signing_key:
|
97
|
+
specification_version: 3
|
98
|
+
summary: An ActiveRecord adapter for PostGIS, based on RGeo.
|
99
|
+
test_files:
|
100
|
+
- test/tc_basic.rb
|