activerecord-spatialite-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 +8 -0
- data/README.rdoc +155 -0
- data/Version +1 -0
- data/lib/active_record/connection_adapters/spatialite_adapter.rb +518 -0
- data/test/tc_basic.rb +216 -0
- metadata +100 -0
data/History.rdoc
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
=== 0.2.0 / 2010-12-07
|
2
|
+
|
3
|
+
* Initial public alpha release. Spun activerecord-spatialite-adapter off from the core rgeo gem.
|
4
|
+
* You can now set the factory for a specific column by name.
|
5
|
+
* Dropping a table that had spatial indexes tied to it didn't remove the index tables. Fixed.
|
6
|
+
* You can now remove spatial indexes by name.
|
7
|
+
|
8
|
+
For earlier history, see the History file for the rgeo gem.
|
data/README.rdoc
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
== SpatiaLite \ActiveRecord Adapter
|
2
|
+
|
3
|
+
The SpatiaLite \ActiveRecord Adapter is an \ActiveRecord connection
|
4
|
+
adapter based on the standard sqlite3 adapter. It extends the standard
|
5
|
+
adapter to provide support for spatial extensions using SpatiaLite, using
|
6
|
+
the {RGeo}[http://github.com/dazuma/rgeo] library to represent spatial
|
7
|
+
data in Ruby. Like the standard sqlite3 adapter, this adapter requires
|
8
|
+
the sqlite3-ruby gem.
|
9
|
+
|
10
|
+
=== Usage
|
11
|
+
|
12
|
+
To use this adapter, add this gem, "activerecord-spatialite-adapter",
|
13
|
+
to your Gemfile, and then request the adapter name "spatialite" in
|
14
|
+
your database connection configuration (which, for a Rails application,
|
15
|
+
is in the config/database.yml file). The other database connection
|
16
|
+
configuration parameters are the same as for the stock sqlite3 adapter,
|
17
|
+
with the exception of one additional parameter, <tt>libspatialite</tt>,
|
18
|
+
which should be set to the full path to the libspatialite shared library,
|
19
|
+
if it is not installed in a standard place (such as /usr/local or
|
20
|
+
/opt/local).
|
21
|
+
|
22
|
+
First, this adapter extends the migration syntax to support creating
|
23
|
+
spatial columns and indexes. To create a spatial column, use the
|
24
|
+
<tt>:geometry</tt> type, or any of the OGC spatial types such as
|
25
|
+
<tt>:point</tt> or <tt>:line_string</tt>. To create a spatial index, set
|
26
|
+
the <tt>:spatial</tt> option to true.
|
27
|
+
|
28
|
+
Examples:
|
29
|
+
|
30
|
+
create_table :spatial_table do |t|
|
31
|
+
t.column :latlon, :point # or t.point :latlon
|
32
|
+
t.line_string :path
|
33
|
+
t.geometry :shape
|
34
|
+
end
|
35
|
+
change_table :spatial_table do |t|
|
36
|
+
t.index :latlon, :spatial => true
|
37
|
+
end
|
38
|
+
|
39
|
+
When this adapter is in use, spatial attributes in your \ActiveRecord
|
40
|
+
objects will have RGeo geometry values. You can set spatial attributes
|
41
|
+
either to RGeo geometry objects, or to strings in WKT (well-known text)
|
42
|
+
format, which the adapter will automatically convert to geometry objects.
|
43
|
+
|
44
|
+
To specify the RGeo geometry factory, you can either set an explicit
|
45
|
+
factory for a column, or provide a factory generator that will yield the
|
46
|
+
appropriate factory for the table's spatial columns based on the value.
|
47
|
+
For the former, call the set_rgeo_factory_for_column class method on your
|
48
|
+
\ActiveRecord class. For the latter, set the rgeo_factory_generator class
|
49
|
+
attribute. This generator should understand at least the <tt>:srid</tt>
|
50
|
+
option, which will be provided based on the column's specified SRID. Note
|
51
|
+
that the spatialite adapter does not currently support Z or M coordinates,
|
52
|
+
as it's unclear to me whether SpatiaLite itself supports them. The
|
53
|
+
set_rgeo_factory_for_column and rgeo_factory_generator methods are
|
54
|
+
actually implemented and documented in the "rgeo-activerecord" gem.
|
55
|
+
|
56
|
+
Examples, given the spatial table defined above:
|
57
|
+
|
58
|
+
class SpatialTable < ActiveRecord::Base
|
59
|
+
|
60
|
+
# By default, use the GEOS implementation for spatial columns.
|
61
|
+
self.rgeo_factory_generator = RGeo::Geos.method(:factory)
|
62
|
+
|
63
|
+
# But use a geographic implementation for the :latlon column.
|
64
|
+
set_rgeo_factory_for_column(:latlon, RGeo::Geographic.spherical_factory)
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
Now you can interact with the data using the RGeo types:
|
69
|
+
|
70
|
+
rec = SpatialTable.new
|
71
|
+
rec.latlon = 'POINT(-122 47)' # You can set by feature object or WKT.
|
72
|
+
loc = rec.latlon # Accessing always returns a feature object, in
|
73
|
+
# this case, a geographic that understands latitude.
|
74
|
+
loc.latitude # => 47
|
75
|
+
rec.shape = loc # the factory for the :shape column is GEOS, so the
|
76
|
+
# value will be cast from geographic to GEOS.
|
77
|
+
RGeo::Geos.is_geos?(rec.shape) # => true
|
78
|
+
|
79
|
+
=== Installation
|
80
|
+
|
81
|
+
This adapter has the following requirements:
|
82
|
+
|
83
|
+
* Ruby 1.8.7 or later. Ruby 1.9.2 or later preferred.
|
84
|
+
* SpatiaLite 2.3 or later (2.4 recommended).
|
85
|
+
* \ActiveRecord 3.0.3 or later. Earlier versions will not work.
|
86
|
+
* rgeo gem 0.2.0 or later.
|
87
|
+
* rgeo-activerecord gem 0.2.0 or later.
|
88
|
+
* sqlite3 gem 1.3 or later.
|
89
|
+
|
90
|
+
Install this adapter as a gem:
|
91
|
+
|
92
|
+
gem install activerecord-spatialite-adapter
|
93
|
+
|
94
|
+
See the README for the "rgeo" gem, a required dependency, for further
|
95
|
+
installation information.
|
96
|
+
|
97
|
+
=== Known bugs and limitations
|
98
|
+
|
99
|
+
The spatialite adapter works in principle, but there are a few known holes
|
100
|
+
in the functionality. Notably, things that require the alter_table
|
101
|
+
mechanism may not function properly, because the current sqlite3
|
102
|
+
implementation doesn't properly preserve triggers. This means, among
|
103
|
+
other things, removing columns in tables with spatial information can
|
104
|
+
cause the remaining spatial columns to fail. However, most simple things
|
105
|
+
work, including creating tables with geometric columns, adding geometric
|
106
|
+
columns to existing tables, and creating and removing spatial R*tree
|
107
|
+
indexes. Note that this adapter is not yet well tested.
|
108
|
+
|
109
|
+
=== Development and support
|
110
|
+
|
111
|
+
Documentation is available at http://virtuoso.rubyforge.org/activerecord-spatialite-adapter/README_rdoc.html
|
112
|
+
|
113
|
+
Source code is hosted on Github at http://github.com/dazuma/activerecord-spatialite-adapter
|
114
|
+
|
115
|
+
Contributions are welcome. Fork the project on Github.
|
116
|
+
|
117
|
+
Report bugs on Github issues at http://github.org/dazuma/activerecord-spatialite-adapter/issues
|
118
|
+
|
119
|
+
Contact the author at dazuma at gmail dot com.
|
120
|
+
|
121
|
+
=== Acknowledgments
|
122
|
+
|
123
|
+
RGeo is written by Daniel Azuma (http://www.daniel-azuma.com).
|
124
|
+
|
125
|
+
Development of RGeo is sponsored by GeoPage, Inc. (http://www.geopage.com).
|
126
|
+
|
127
|
+
=== License
|
128
|
+
|
129
|
+
Copyright 2010 Daniel Azuma
|
130
|
+
|
131
|
+
All rights reserved.
|
132
|
+
|
133
|
+
Redistribution and use in source and binary forms, with or without
|
134
|
+
modification, are permitted provided that the following conditions are met:
|
135
|
+
|
136
|
+
* Redistributions of source code must retain the above copyright notice,
|
137
|
+
this list of conditions and the following disclaimer.
|
138
|
+
* Redistributions in binary form must reproduce the above copyright notice,
|
139
|
+
this list of conditions and the following disclaimer in the documentation
|
140
|
+
and/or other materials provided with the distribution.
|
141
|
+
* Neither the name of the copyright holder, nor the names of any other
|
142
|
+
contributors to this software, may be used to endorse or promote products
|
143
|
+
derived from this software without specific prior written permission.
|
144
|
+
|
145
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
146
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
147
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
148
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
149
|
+
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
150
|
+
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
151
|
+
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
152
|
+
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
153
|
+
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
154
|
+
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
155
|
+
POSSIBILITY OF SUCH DAMAGE.
|
data/Version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.0
|
@@ -0,0 +1,518 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
#
|
3
|
+
# SpatiaLite 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/sqlite3_adapter'
|
39
|
+
|
40
|
+
|
41
|
+
# :stopdoc:
|
42
|
+
|
43
|
+
module Arel
|
44
|
+
module Visitors
|
45
|
+
VISITORS['spatialite'] = ::Arel::Visitors::SQLite
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# :startdoc:
|
50
|
+
|
51
|
+
|
52
|
+
# The activerecord-spatialite-adapter gem installs the *spatialite*
|
53
|
+
# connection adapter into ActiveRecord.
|
54
|
+
|
55
|
+
module ActiveRecord
|
56
|
+
|
57
|
+
|
58
|
+
# ActiveRecord looks for the spatialite_connection factory method in
|
59
|
+
# this class.
|
60
|
+
|
61
|
+
class Base
|
62
|
+
|
63
|
+
|
64
|
+
# Create a spatialite connection adapter.
|
65
|
+
|
66
|
+
def self.spatialite_connection(config_)
|
67
|
+
unless 'spatialite' == config_[:adapter]
|
68
|
+
raise ::ArgumentError, 'adapter name should be "spatialite"'
|
69
|
+
end
|
70
|
+
unless config_[:database]
|
71
|
+
raise ::ArgumentError, "No database file specified. Missing argument: database"
|
72
|
+
end
|
73
|
+
|
74
|
+
# Allow database path relative to Rails.root, but only if
|
75
|
+
# the database path is not the special path that tells
|
76
|
+
# Sqlite to build a database only in memory.
|
77
|
+
if defined?(::Rails.root) && ':memory:' != config_[:database]
|
78
|
+
config_[:database] = ::File.expand_path(config_[:database], ::Rails.root)
|
79
|
+
end
|
80
|
+
|
81
|
+
unless self.class.const_defined?(:SQLite3)
|
82
|
+
require_library_or_gem('sqlite3')
|
83
|
+
end
|
84
|
+
db_ = ::SQLite3::Database.new(config_[:database], :results_as_hash => true)
|
85
|
+
db_.busy_timeout(config_[:timeout]) unless config_[:timeout].nil?
|
86
|
+
|
87
|
+
# Load SpatiaLite
|
88
|
+
path_ = config_[:libspatialite]
|
89
|
+
if path_ && (!::File.file?(path_) || !::File.readable?(path_))
|
90
|
+
raise "Cannot read libspatialite library at #{path_}"
|
91
|
+
end
|
92
|
+
unless path_
|
93
|
+
prefixes_ = ['/usr/local/spatialite', '/usr/local/libspatialite', '/usr/local', '/opt/local', '/sw/local', '/usr']
|
94
|
+
suffixes_ = ['so', 'dylib'].join(',')
|
95
|
+
prefixes_.each do |prefix_|
|
96
|
+
pa_ = ::Dir.glob("#{prefix_}/lib/libspatialite.{#{suffixes_}}")
|
97
|
+
if pa_.size > 0
|
98
|
+
path_ = pa_.first
|
99
|
+
break
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
unless path_
|
104
|
+
raise 'Cannot find libspatialite in the usual places. Please provide the path in the "libspatialite" config parameter.'
|
105
|
+
end
|
106
|
+
db_.enable_load_extension(1)
|
107
|
+
db_.load_extension(path_)
|
108
|
+
|
109
|
+
ConnectionAdapters::SpatiaLiteAdapter.new(db_, logger, config_)
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
module ConnectionAdapters # :nodoc:
|
117
|
+
|
118
|
+
|
119
|
+
class SpatiaLiteAdapter < SQLite3Adapter # :nodoc:
|
120
|
+
|
121
|
+
|
122
|
+
ADAPTER_NAME = 'SpatiaLite'.freeze
|
123
|
+
|
124
|
+
@@native_database_types = nil
|
125
|
+
|
126
|
+
|
127
|
+
def native_database_types
|
128
|
+
unless @@native_database_types
|
129
|
+
@@native_database_types = super.dup
|
130
|
+
@@native_database_types.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"})
|
131
|
+
end
|
132
|
+
@@native_database_types
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
def adapter_name
|
137
|
+
ADAPTER_NAME
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
def spatialite_version
|
142
|
+
@spatialite_version ||= SQLiteAdapter::Version.new(select_value('SELECT spatialite_version()'))
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
def quote(value_, column_=nil)
|
147
|
+
if ::RGeo::Feature::Geometry.check_type(value_)
|
148
|
+
"GeomFromWKB(X'#{::RGeo::WKRep::WKBGenerator.new(:hex_format => true).generate(value_)}', #{value_.srid})"
|
149
|
+
else
|
150
|
+
super
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
|
155
|
+
def columns(table_name_, name_=nil) #:nodoc:
|
156
|
+
spatial_info_ = spatial_column_info(table_name_)
|
157
|
+
table_structure(table_name_).map do |field_|
|
158
|
+
col_ = SpatialColumn.new(field_['name'], field_['dflt_value'], field_['type'], field_['notnull'].to_i == 0)
|
159
|
+
info_ = spatial_info_[field_['name']]
|
160
|
+
if info_
|
161
|
+
col_.set_srid(info_[:srid])
|
162
|
+
end
|
163
|
+
col_
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
|
168
|
+
def spatial_indexes(table_name_, name_=nil)
|
169
|
+
table_name_ = table_name_.to_s
|
170
|
+
names_ = select_values("SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 'idx_#{quote_string(table_name_)}_%' AND rootpage=0") || []
|
171
|
+
names_.map do |name_|
|
172
|
+
col_name_ = name_.sub("idx_#{table_name_}_", '')
|
173
|
+
::RGeo::ActiveRecord::SpatialIndexDefinition.new(table_name_, name_, false, [col_name_], [], true)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
|
178
|
+
def create_table(table_name_, options_={})
|
179
|
+
table_name_ = table_name_.to_s
|
180
|
+
table_definition_ = SpatialTableDefinition.new(self)
|
181
|
+
table_definition_.primary_key(options_[:primary_key] || ::ActiveRecord::Base.get_primary_key(table_name_.singularize)) unless options_[:id] == false
|
182
|
+
yield table_definition_ if block_given?
|
183
|
+
if options_[:force] && table_exists?(table_name_)
|
184
|
+
drop_table(table_name_, options_)
|
185
|
+
end
|
186
|
+
|
187
|
+
create_sql_ = "CREATE#{' TEMPORARY' if options_[:temporary]} TABLE "
|
188
|
+
create_sql_ << "#{quote_table_name(table_name_)} ("
|
189
|
+
create_sql_ << table_definition_.to_sql
|
190
|
+
create_sql_ << ") #{options_[:options]}"
|
191
|
+
execute create_sql_
|
192
|
+
|
193
|
+
table_definition_.spatial_columns.each do |col_|
|
194
|
+
execute("SELECT AddGeometryColumn('#{quote_string(table_name_)}', '#{quote_string(col_.name.to_s)}', #{col_.srid}, '#{quote_string(col_.type.to_s.gsub('_','').upcase)}', 'XY', #{col_.null ? 0 : 1})")
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
|
199
|
+
def drop_table(table_name_, options_={})
|
200
|
+
spatial_indexes(table_name_).each do |index_|
|
201
|
+
remove_index(table_name_, :spatial => true, :column => index_.columns[0])
|
202
|
+
end
|
203
|
+
execute("DELETE from geometry_columns where f_table_name='#{quote_string(table_name_.to_s)}'")
|
204
|
+
super
|
205
|
+
end
|
206
|
+
|
207
|
+
|
208
|
+
def add_column(table_name_, column_name_, type_, options_={})
|
209
|
+
if ::RGeo::ActiveRecord::GEOMETRY_TYPES.include?(type_.to_sym)
|
210
|
+
execute("SELECT AddGeometryColumn('#{quote_string(table_name_.to_s)}', '#{quote_string(column_name_.to_s)}', #{options_[:srid].to_i}, '#{quote_string(type_.to_s)}', 'XY', #{options_[:null] == false ? 0 : 1})")
|
211
|
+
else
|
212
|
+
super
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
|
217
|
+
def add_index(table_name_, column_name_, options_={})
|
218
|
+
if options_[:spatial]
|
219
|
+
column_name_ = column_name_.first if column_name_.kind_of?(::Array) && column_name_.size == 1
|
220
|
+
table_name_ = table_name_.to_s
|
221
|
+
column_name_ = column_name_.to_s
|
222
|
+
spatial_info_ = spatial_column_info(table_name_)
|
223
|
+
unless spatial_info_[column_name_]
|
224
|
+
raise ::ArgumentError, "Can't create spatial index because column '#{column_name_}' in table '#{table_name_}' is not a geometry column"
|
225
|
+
end
|
226
|
+
result_ = select_value("SELECT CreateSpatialIndex('#{quote_string(table_name_)}', '#{quote_string(column_name_)}')").to_i
|
227
|
+
if result_ == 0
|
228
|
+
raise ::ArgumentError, "Spatial index already exists on table '#{table_name_}', column '#{column_name_}'"
|
229
|
+
end
|
230
|
+
result_
|
231
|
+
else
|
232
|
+
super
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
|
237
|
+
def remove_index(table_name_, options_={})
|
238
|
+
if options_[:spatial]
|
239
|
+
table_name_ = table_name_.to_s
|
240
|
+
column_ = options_[:column]
|
241
|
+
if column_
|
242
|
+
column_ = column_[0] if column_.kind_of?(::Array)
|
243
|
+
column_ = column_.to_s
|
244
|
+
else
|
245
|
+
index_name_ = options_[:name]
|
246
|
+
unless index_name_
|
247
|
+
raise ::ArgumentError, "You need to specify a column or index name to remove a spatial index."
|
248
|
+
end
|
249
|
+
if index_name_ =~ /^idx_#{table_name_}_(\w+)$/
|
250
|
+
column_ = $1
|
251
|
+
else
|
252
|
+
raise ::ArgumentError, "Unknown spatial index name: #{index_name_.inspect}."
|
253
|
+
end
|
254
|
+
end
|
255
|
+
spatial_info_ = spatial_column_info(table_name_)
|
256
|
+
unless spatial_info_[column_]
|
257
|
+
raise ::ArgumentError, "Can't remove spatial index because column '#{column_}' in table '#{table_name_}' is not a geometry column"
|
258
|
+
end
|
259
|
+
index_name_ = "idx_#{table_name_}_#{column_}"
|
260
|
+
has_index_ = select_value("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='#{quote_string(index_name_)}'").to_i > 0
|
261
|
+
unless has_index_
|
262
|
+
raise ::ArgumentError, "Spatial index not present on table '#{table_name_}', column '#{column_}'"
|
263
|
+
end
|
264
|
+
execute("SELECT DisableSpatialIndex('#{quote_string(table_name_)}', '#{quote_string(column_)}')")
|
265
|
+
execute("DROP TABLE #{quote_table_name(index_name_)}")
|
266
|
+
else
|
267
|
+
super
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
|
272
|
+
def spatial_column_info(table_name_)
|
273
|
+
info_ = execute("SELECT * FROM geometry_columns WHERE f_table_name='#{quote_string(table_name_.to_s)}'")
|
274
|
+
result_ = {}
|
275
|
+
info_.each do |row_|
|
276
|
+
result_[row_['f_geometry_column']] = {
|
277
|
+
:name => row_['f_geometry_column'],
|
278
|
+
:type => row_['type'],
|
279
|
+
:dimension => row_['coord_dimension'],
|
280
|
+
:srid => row_['srid'],
|
281
|
+
:has_index => row_['spatial_index_enabled'],
|
282
|
+
}
|
283
|
+
end
|
284
|
+
result_
|
285
|
+
end
|
286
|
+
|
287
|
+
|
288
|
+
class SpatialTableDefinition < ConnectionAdapters::TableDefinition # :nodoc:
|
289
|
+
|
290
|
+
attr_reader :spatial_columns
|
291
|
+
|
292
|
+
def initialize(base_)
|
293
|
+
super
|
294
|
+
end
|
295
|
+
|
296
|
+
def column(name_, type_, options_={})
|
297
|
+
super
|
298
|
+
col_ = self[name_]
|
299
|
+
if ::RGeo::ActiveRecord::GEOMETRY_TYPES.include?(col_.type.to_sym)
|
300
|
+
col_.extend(GeometricColumnDefinitionMethods) unless col_.respond_to?(:srid)
|
301
|
+
col_.set_srid(options_[:srid].to_i)
|
302
|
+
end
|
303
|
+
self
|
304
|
+
end
|
305
|
+
|
306
|
+
def to_sql
|
307
|
+
@columns.find_all{ |c_| !c_.respond_to?(:srid) }.map{ |c_| c_.to_sql } * ', '
|
308
|
+
end
|
309
|
+
|
310
|
+
def spatial_columns
|
311
|
+
@columns.find_all{ |c_| c_.respond_to?(:srid) }
|
312
|
+
end
|
313
|
+
|
314
|
+
end
|
315
|
+
|
316
|
+
|
317
|
+
module GeometricColumnDefinitionMethods # :nodoc:
|
318
|
+
|
319
|
+
def srid
|
320
|
+
defined?(@srid) ? @srid : 4326
|
321
|
+
end
|
322
|
+
|
323
|
+
def set_srid(value_)
|
324
|
+
@srid = value_
|
325
|
+
end
|
326
|
+
|
327
|
+
end
|
328
|
+
|
329
|
+
|
330
|
+
class SpatialColumn < ConnectionAdapters::SQLiteColumn # :nodoc:
|
331
|
+
|
332
|
+
|
333
|
+
def initialize(name_, default_, sql_type_=nil, null_=true)
|
334
|
+
super(name_, default_, sql_type_, null_)
|
335
|
+
@geometric_type = ::RGeo::ActiveRecord.geometric_type_from_name(sql_type_)
|
336
|
+
@ar_class = ::ActiveRecord::Base
|
337
|
+
@srid = 0
|
338
|
+
end
|
339
|
+
|
340
|
+
|
341
|
+
def set_ar_class(val_)
|
342
|
+
@ar_class = val_
|
343
|
+
end
|
344
|
+
|
345
|
+
def set_srid(val_)
|
346
|
+
@srid = val_
|
347
|
+
end
|
348
|
+
|
349
|
+
|
350
|
+
attr_reader :srid
|
351
|
+
attr_reader :geometric_type
|
352
|
+
|
353
|
+
|
354
|
+
def spatial?
|
355
|
+
type == :geometry
|
356
|
+
end
|
357
|
+
|
358
|
+
|
359
|
+
def klass
|
360
|
+
type == :geometry ? ::RGeo::Feature::Geometry : super
|
361
|
+
end
|
362
|
+
|
363
|
+
|
364
|
+
def type_cast(value_)
|
365
|
+
type == :geometry ? SpatialColumn.convert_to_geometry(value_, @ar_class, name, @srid) : super
|
366
|
+
end
|
367
|
+
|
368
|
+
|
369
|
+
def type_cast_code(var_name_)
|
370
|
+
type == :geometry ? "::ActiveRecord::ConnectionAdapters::SpatiaLiteAdapter::SpatialColumn.convert_to_geometry(#{var_name_}, self.class, #{name.inspect}, #{@srid})" : super
|
371
|
+
end
|
372
|
+
|
373
|
+
|
374
|
+
private
|
375
|
+
|
376
|
+
|
377
|
+
def simplified_type(sql_type_)
|
378
|
+
sql_type_ =~ /geometry|point|linestring|polygon/i ? :geometry : super
|
379
|
+
end
|
380
|
+
|
381
|
+
|
382
|
+
def self.convert_to_geometry(input_, ar_class_, column_name_, column_srid_)
|
383
|
+
case input_
|
384
|
+
when ::RGeo::Feature::Geometry
|
385
|
+
factory_ = ar_class_.rgeo_factory_for_column(column_name_, :srid => column_srid_)
|
386
|
+
::RGeo::Feature.cast(input_, factory_)
|
387
|
+
when ::String
|
388
|
+
if input_.length == 0
|
389
|
+
nil
|
390
|
+
else
|
391
|
+
factory_ = ar_class_.rgeo_factory_for_column(column_name_, :srid => column_srid_)
|
392
|
+
if input_[0,1] == "\x00"
|
393
|
+
NativeFormatParser.new(factory_).parse(input_) rescue nil
|
394
|
+
else
|
395
|
+
::RGeo::WKRep::WKTParser.new(factory_, :support_ewkt => true).parse(input_)
|
396
|
+
end
|
397
|
+
end
|
398
|
+
else
|
399
|
+
nil
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
|
404
|
+
end
|
405
|
+
|
406
|
+
|
407
|
+
class NativeFormatParser # :nodoc:
|
408
|
+
|
409
|
+
|
410
|
+
def initialize(factory_)
|
411
|
+
@factory = factory_
|
412
|
+
end
|
413
|
+
|
414
|
+
|
415
|
+
def parse(data_)
|
416
|
+
@little_endian = data_[1,1] == "\x01"
|
417
|
+
srid_ = data_[2,4].unpack(@little_endian ? 'V' : 'N').first
|
418
|
+
begin
|
419
|
+
_start_scanner(data_)
|
420
|
+
obj_ = _parse_object(false)
|
421
|
+
_get_byte(0xfe)
|
422
|
+
ensure
|
423
|
+
_clean_scanner
|
424
|
+
end
|
425
|
+
obj_
|
426
|
+
end
|
427
|
+
|
428
|
+
|
429
|
+
def _parse_object(contained_)
|
430
|
+
_get_byte(contained_ ? 0x69 : 0x7c)
|
431
|
+
type_code_ = _get_integer
|
432
|
+
case type_code_
|
433
|
+
when 1
|
434
|
+
coords_ = _get_doubles(2)
|
435
|
+
@factory.point(*coords_)
|
436
|
+
when 2
|
437
|
+
_parse_line_string
|
438
|
+
when 3
|
439
|
+
interior_rings_ = (1.._get_integer).map{ _parse_line_string }
|
440
|
+
exterior_ring_ = interior_rings_.shift || @factory.linear_ring([])
|
441
|
+
@factory.polygon(exterior_ring_, interior_rings_)
|
442
|
+
when 4
|
443
|
+
@factory.multi_point((1.._get_integer).map{ _parse_object(1) })
|
444
|
+
when 5
|
445
|
+
@factory.multi_line_string((1.._get_integer).map{ _parse_object(2) })
|
446
|
+
when 6
|
447
|
+
@factory.multi_polygon((1.._get_integer).map{ _parse_object(3) })
|
448
|
+
when 7
|
449
|
+
@factory.collection((1.._get_integer).map{ _parse_object(true) })
|
450
|
+
else
|
451
|
+
raise ::RGeo::Error::ParseError, "Unknown type value: #{type_code_}."
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
|
456
|
+
def _parse_line_string
|
457
|
+
count_ = _get_integer
|
458
|
+
coords_ = _get_doubles(2 * count_)
|
459
|
+
@factory.line_string((0...count_).map{ |i_| @factory.point(*coords_[2*i_,2]) })
|
460
|
+
end
|
461
|
+
|
462
|
+
|
463
|
+
def _start_scanner(data_)
|
464
|
+
@_data = data_
|
465
|
+
@_len = data_.length
|
466
|
+
@_pos = 38
|
467
|
+
end
|
468
|
+
|
469
|
+
|
470
|
+
def _clean_scanner
|
471
|
+
@_data = nil
|
472
|
+
end
|
473
|
+
|
474
|
+
|
475
|
+
def _get_byte(expect_=nil)
|
476
|
+
if @_pos + 1 > @_len
|
477
|
+
raise ::RGeo::Error::ParseError, "Not enough bytes left to fulfill 1 byte"
|
478
|
+
end
|
479
|
+
str_ = @_data[@_pos, 1]
|
480
|
+
@_pos += 1
|
481
|
+
val_ = str_.unpack("C").first
|
482
|
+
if expect_ && expect_ != val_
|
483
|
+
raise ::RGeo::Error::ParseError, "Expected byte 0x#{expect_.to_s(16)} but got 0x#{val_.to_s(16)}"
|
484
|
+
end
|
485
|
+
val_
|
486
|
+
end
|
487
|
+
|
488
|
+
|
489
|
+
def _get_integer
|
490
|
+
if @_pos + 4 > @_len
|
491
|
+
raise ::RGeo::Error::ParseError, "Not enough bytes left to fulfill 1 integer"
|
492
|
+
end
|
493
|
+
str_ = @_data[@_pos, 4]
|
494
|
+
@_pos += 4
|
495
|
+
str_.unpack("#{@little_endian ? 'V' : 'N'}").first
|
496
|
+
end
|
497
|
+
|
498
|
+
|
499
|
+
def _get_doubles(count_)
|
500
|
+
len_ = 8 * count_
|
501
|
+
if @_pos + len_ > @_len
|
502
|
+
raise ::RGeo::Error::ParseError, "Not enough bytes left to fulfill #{count_} doubles"
|
503
|
+
end
|
504
|
+
str_ = @_data[@_pos, len_]
|
505
|
+
@_pos += len_
|
506
|
+
str_.unpack("#{@little_endian ? 'E' : 'G'}*")
|
507
|
+
end
|
508
|
+
|
509
|
+
|
510
|
+
end
|
511
|
+
|
512
|
+
|
513
|
+
end
|
514
|
+
|
515
|
+
end
|
516
|
+
|
517
|
+
|
518
|
+
end
|
data/test/tc_basic.rb
ADDED
@@ -0,0 +1,216 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
#
|
3
|
+
# Tests for the MysqlSpatial 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 SpatiaLiteAdapter # :nodoc:
|
43
|
+
module Tests # :nodoc:
|
44
|
+
|
45
|
+
class TestBasic < ::Test::Unit::TestCase # :nodoc:
|
46
|
+
|
47
|
+
|
48
|
+
DATABASE_CONFIG_PATH = ::File.dirname(__FILE__)+'/database.yml'
|
49
|
+
|
50
|
+
def self.before_open_database(params_)
|
51
|
+
database_ = params_[:config][:database]
|
52
|
+
dir_ = ::File.dirname(database_)
|
53
|
+
::FileUtils.mkdir_p(dir_) unless dir_ == '.'
|
54
|
+
::FileUtils.rm_f(database_)
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.initialize_database(params_)
|
58
|
+
params_[:connection].execute('SELECT InitSpatialMetaData()')
|
59
|
+
end
|
60
|
+
|
61
|
+
include AdapterTestHelper
|
62
|
+
|
63
|
+
|
64
|
+
define_test_methods do
|
65
|
+
|
66
|
+
|
67
|
+
def populate_ar_class(content_)
|
68
|
+
klass_ = create_ar_class
|
69
|
+
case content_
|
70
|
+
when :latlon_point
|
71
|
+
klass_.connection.create_table(:spatial_test) do |t_|
|
72
|
+
t_.column 'latlon', :point, :srid => 4326
|
73
|
+
end
|
74
|
+
end
|
75
|
+
klass_
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
def test_meta_data_present
|
80
|
+
result_ = DEFAULT_AR_CLASS.connection.select_value("SELECT COUNT(*) FROM spatial_ref_sys").to_i
|
81
|
+
assert_not_equal(0, result_)
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
def test_create_simple_geometry
|
86
|
+
klass_ = create_ar_class
|
87
|
+
klass_.connection.create_table(:spatial_test) do |t_|
|
88
|
+
t_.column 'latlon', :geometry
|
89
|
+
end
|
90
|
+
assert_equal(1, klass_.connection.select_value("SELECT COUNT(*) FROM geometry_columns WHERE f_table_name='spatial_test'").to_i)
|
91
|
+
assert_equal(::RGeo::Feature::Geometry, klass_.columns.last.geometric_type)
|
92
|
+
assert(klass_.cached_attributes.include?('latlon'))
|
93
|
+
klass_.connection.drop_table(:spatial_test)
|
94
|
+
assert_equal(0, klass_.connection.select_value("SELECT COUNT(*) FROM geometry_columns WHERE f_table_name='spatial_test'").to_i)
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
def test_create_point_geometry
|
99
|
+
klass_ = create_ar_class
|
100
|
+
klass_.connection.create_table(:spatial_test) do |t_|
|
101
|
+
t_.column 'latlon', :point
|
102
|
+
end
|
103
|
+
assert_equal(::RGeo::Feature::Point, klass_.columns.last.geometric_type)
|
104
|
+
assert(klass_.cached_attributes.include?('latlon'))
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
def test_create_geometry_with_index
|
109
|
+
klass_ = create_ar_class
|
110
|
+
klass_.connection.create_table(:spatial_test) do |t_|
|
111
|
+
t_.column 'latlon', :geometry
|
112
|
+
end
|
113
|
+
klass_.connection.change_table(:spatial_test) do |t_|
|
114
|
+
t_.index([:latlon], :spatial => true)
|
115
|
+
end
|
116
|
+
assert(klass_.connection.spatial_indexes(:spatial_test).last.spatial)
|
117
|
+
assert_equal(1, klass_.connection.select_value("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='idx_spatial_test_latlon'").to_i)
|
118
|
+
klass_.connection.drop_table(:spatial_test)
|
119
|
+
assert_equal(0, klass_.connection.select_value("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='idx_spatial_test_latlon'").to_i)
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
def test_set_and_get_point
|
124
|
+
klass_ = populate_ar_class(:latlon_point)
|
125
|
+
obj_ = klass_.new
|
126
|
+
assert_nil(obj_.latlon)
|
127
|
+
obj_.latlon = @factory.point(1, 2)
|
128
|
+
assert_equal(@factory.point(1, 2), obj_.latlon)
|
129
|
+
assert_equal(4326, obj_.latlon.srid)
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
def test_set_and_get_point_from_wkt
|
134
|
+
klass_ = populate_ar_class(:latlon_point)
|
135
|
+
obj_ = klass_.new
|
136
|
+
assert_nil(obj_.latlon)
|
137
|
+
obj_.latlon = 'POINT(1 2)'
|
138
|
+
assert_equal(@factory.point(1, 2), obj_.latlon)
|
139
|
+
assert_equal(4326, obj_.latlon.srid)
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
def test_save_and_load_point
|
144
|
+
klass_ = populate_ar_class(:latlon_point)
|
145
|
+
obj_ = klass_.new
|
146
|
+
obj_.latlon = @factory.point(1, 2)
|
147
|
+
obj_.save!
|
148
|
+
id_ = obj_.id
|
149
|
+
obj2_ = klass_.find(id_)
|
150
|
+
assert_equal(@factory.point(1, 2), obj2_.latlon)
|
151
|
+
assert_equal(4326, obj2_.latlon.srid)
|
152
|
+
end
|
153
|
+
|
154
|
+
|
155
|
+
def test_save_and_load_point_from_wkt
|
156
|
+
klass_ = populate_ar_class(:latlon_point)
|
157
|
+
obj_ = klass_.new
|
158
|
+
obj_.latlon = 'POINT(1 2)'
|
159
|
+
obj_.save!
|
160
|
+
id_ = obj_.id
|
161
|
+
obj2_ = klass_.find(id_)
|
162
|
+
assert_equal(@factory.point(1, 2), obj2_.latlon)
|
163
|
+
assert_equal(4326, obj2_.latlon.srid)
|
164
|
+
end
|
165
|
+
|
166
|
+
|
167
|
+
def test_add_column
|
168
|
+
klass_ = create_ar_class
|
169
|
+
klass_.connection.create_table(:spatial_test) do |t_|
|
170
|
+
t_.column('latlon', :geometry)
|
171
|
+
end
|
172
|
+
klass_.connection.change_table(:spatial_test) do |t_|
|
173
|
+
t_.column('geom2', :point, :srid => 4326)
|
174
|
+
t_.column('name', :string)
|
175
|
+
end
|
176
|
+
assert_equal(2, klass_.connection.select_value("SELECT COUNT(*) FROM geometry_columns WHERE f_table_name='spatial_test'").to_i)
|
177
|
+
cols_ = klass_.columns
|
178
|
+
assert_equal(::RGeo::Feature::Geometry, cols_[-3].geometric_type)
|
179
|
+
assert_equal(-1, cols_[-3].srid)
|
180
|
+
assert_equal(::RGeo::Feature::Point, cols_[-2].geometric_type)
|
181
|
+
assert_equal(4326, cols_[-2].srid)
|
182
|
+
assert_nil(cols_[-1].geometric_type)
|
183
|
+
end
|
184
|
+
|
185
|
+
|
186
|
+
def test_readme_example
|
187
|
+
klass_ = create_ar_class
|
188
|
+
klass_.connection.create_table(:spatial_test) do |t_|
|
189
|
+
t_.column(:latlon, :point)
|
190
|
+
t_.line_string(:path)
|
191
|
+
t_.geometry(:shape)
|
192
|
+
end
|
193
|
+
klass_.connection.change_table(:spatial_test) do |t_|
|
194
|
+
t_.index(:latlon, :spatial => true)
|
195
|
+
end
|
196
|
+
klass_.class_eval do
|
197
|
+
self.rgeo_factory_generator = ::RGeo::Geos.method(:factory)
|
198
|
+
set_rgeo_factory_for_column(:latlon, ::RGeo::Geographic.spherical_factory)
|
199
|
+
end
|
200
|
+
rec_ = klass_.new
|
201
|
+
rec_.latlon = 'POINT(-122 47)'
|
202
|
+
loc_ = rec_.latlon
|
203
|
+
assert_equal(47, loc_.latitude)
|
204
|
+
rec_.shape = loc_
|
205
|
+
assert_equal(true, ::RGeo::Geos.is_geos?(rec_.shape))
|
206
|
+
end
|
207
|
+
|
208
|
+
|
209
|
+
end
|
210
|
+
|
211
|
+
end
|
212
|
+
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
metadata
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: activerecord-spatialite-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: sqlite3-ruby
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
segments:
|
44
|
+
- 1
|
45
|
+
- 3
|
46
|
+
- 2
|
47
|
+
version: 1.3.2
|
48
|
+
type: :runtime
|
49
|
+
version_requirements: *id002
|
50
|
+
description: This is an ActiveRecord connection adapter for the SpatiaLite extension to the Sqlite3 database. It is based on the stock sqlite3 adapter, but provides built-in support for spatial databases using SpatiaLite. 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/spatialite_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-spatialite-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 SpatiaLite, based on RGeo.
|
99
|
+
test_files:
|
100
|
+
- test/tc_basic.rb
|