activerecord-spatial 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +16 -0
  3. data/Gemfile +21 -0
  4. data/Guardfile +17 -0
  5. data/MIT-LICENSE +23 -0
  6. data/README.rdoc +169 -0
  7. data/Rakefile +28 -0
  8. data/activerecord-spatial.gemspec +26 -0
  9. data/lib/activerecord-spatial.rb +32 -0
  10. data/lib/activerecord-spatial/active_record.rb +14 -0
  11. data/lib/activerecord-spatial/active_record/connection_adapters/postgresql/adapter_extensions.rb +36 -0
  12. data/lib/activerecord-spatial/active_record/connection_adapters/postgresql/postgis.rb +24 -0
  13. data/lib/activerecord-spatial/active_record/connection_adapters/postgresql/unknown_srid.rb +21 -0
  14. data/lib/activerecord-spatial/active_record/models/geography_column.rb +17 -0
  15. data/lib/activerecord-spatial/active_record/models/geometry_column.rb +17 -0
  16. data/lib/activerecord-spatial/active_record/models/spatial_column.rb +22 -0
  17. data/lib/activerecord-spatial/active_record/models/spatial_ref_sys.rb +20 -0
  18. data/lib/activerecord-spatial/associations.rb +292 -0
  19. data/lib/activerecord-spatial/spatial_columns.rb +345 -0
  20. data/lib/activerecord-spatial/spatial_function.rb +201 -0
  21. data/lib/activerecord-spatial/spatial_scope_constants.rb +114 -0
  22. data/lib/activerecord-spatial/spatial_scopes.rb +297 -0
  23. data/lib/activerecord-spatial/version.rb +5 -0
  24. data/lib/tasks/test.rake +45 -0
  25. data/test/accessors_geographies_tests.rb +149 -0
  26. data/test/accessors_geometries_tests.rb +151 -0
  27. data/test/adapter_tests.rb +44 -0
  28. data/test/associations_tests.rb +656 -0
  29. data/test/database.yml +17 -0
  30. data/test/fixtures/bars.yml +16 -0
  31. data/test/fixtures/blorts.yml +37 -0
  32. data/test/fixtures/foo3ds.yml +17 -0
  33. data/test/fixtures/foo_geographies.yml +16 -0
  34. data/test/fixtures/foos.yml +16 -0
  35. data/test/fixtures/zortables.yml +36 -0
  36. data/test/geography_column_tests.rb +40 -0
  37. data/test/geometry_column_tests.rb +40 -0
  38. data/test/models/bar.rb +17 -0
  39. data/test/models/blort.rb +12 -0
  40. data/test/models/foo.rb +17 -0
  41. data/test/models/foo3d.rb +17 -0
  42. data/test/models/foo_geography.rb +16 -0
  43. data/test/models/zortable.rb +17 -0
  44. data/test/spatial_scopes_geographies_tests.rb +106 -0
  45. data/test/spatial_scopes_tests.rb +444 -0
  46. data/test/test_helper.rb +272 -0
  47. metadata +138 -0
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NzVhNGNkYTg1MTBkYTM3MGFmYjI2NTBkOTEyMzRlNDhmZDZhMTM3ZQ==
5
+ data.tar.gz: !binary |-
6
+ MmUyYzQ0ZjU3ZDllYjhjOGFhNWY3ZmU5OTY1NDRkMjk2YzY1NmFlOQ==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ Yjk3MzU1MTllMzdkOWM4OWI2ZGQwNjUzYWQyMzI3ZTQ4MjAzNGRjZWYyN2Vi
10
+ YjNkYjZlYzAwNzk3MTFkZWEzMGMwMjNmODE2ZTgzYzI1NzFlNjEzMWIzZjRh
11
+ NjFlY2QxMzI1MTY5MTJiMTYzMzgwNGVjZWZjMDU2NTZlNWFhZGE=
12
+ data.tar.gz: !binary |-
13
+ OGI3NjI0ZDI5M2JjOWJiOWYxZDExYTJkMTNmMzg1NDYyNjZkZjhmZjA5NmIy
14
+ OGM4OTIxYWYxMTExYWQ3MGZjNmJkNmY0NTEzMzM4MTZlNTc2YTRjYjBhMDlm
15
+ NzgxOGIxYTUxMDQ0MGRhNTBhNjE0YzE4ZGQ4ZGU3YmQxMDBlNDI=
data/.gitignore ADDED
@@ -0,0 +1,16 @@
1
+ *.orig
2
+ .*.swp
3
+ .*.swo
4
+ *.tmp
5
+ *.patch
6
+ *~
7
+ .DS_Store
8
+ Thumbs.db
9
+ Gemfile.lock
10
+ Gemfile.local
11
+ Guardfile.local
12
+ debug.log
13
+ test/local_database.yml
14
+ doc/
15
+ pkg/
16
+ coverage/
data/Gemfile ADDED
@@ -0,0 +1,21 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ if RUBY_PLATFORM == "java"
6
+ gem "activerecord-jdbcpostgresql-adapter"
7
+ else
8
+ gem "pg"
9
+ end
10
+
11
+ gem "rdoc", "~> 3.12"
12
+ gem "rake", "~> 10.0"
13
+ gem "minitest"
14
+ gem "minitest-reporters"
15
+ gem "guard-minitest"
16
+ gem "simplecov"
17
+
18
+ if File.exists?('Gemfile.local')
19
+ instance_eval File.read('Gemfile.local')
20
+ end
21
+
data/Guardfile ADDED
@@ -0,0 +1,17 @@
1
+
2
+ guard 'minitest', :test_folders => 'test', :test_file_patterns => '*_tests.rb' do
3
+ watch(%r|^test/(.+)_tests\.rb|)
4
+
5
+ watch(%r|^lib/(.*)([^/]+)\.rb|) do |m|
6
+ "test/#{m[1]}#{m[2]}_tests.rb"
7
+ end
8
+
9
+ watch(%r|^test/test_helper\.rb|) do
10
+ "test"
11
+ end
12
+ end
13
+
14
+ if File.exists?('Guardfile.local')
15
+ instance_eval File.read('Guardfile.local')
16
+ end
17
+
data/MIT-LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2013 2167961 Ontario Inc., Zoocasa <code@zoocasa.com>
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
data/README.rdoc ADDED
@@ -0,0 +1,169 @@
1
+
2
+ = ActiveRecord Spatial
3
+
4
+ ActiveRecord Spatial is a collection of spatially-aware extensions that help
5
+ ActiveRecord work the spatial tables. This gem used to be integrated with our
6
+ geos-extensions gem available at https://github.com/zoocasa/geos-extensions but
7
+ we have separated the two and extended ActiveRecord Spatial's capabilities
8
+ in the process.
9
+
10
+ At the moment, ActiveRecord Spatial is focused completely on PostgreSQL and
11
+ PostGIS databases. This may change in the future, but for our purposes,
12
+ PostGIS is our spatial database of choice and we haven't really had the need
13
+ to use any of the alternatives such as Spatialite or the MySQL spatial
14
+ extensions.
15
+
16
+ == Features
17
+
18
+ * automatic detection of geometry columns and just-in-time conversions
19
+ for input and output to and from WKB when using PostGIS. This allows
20
+ you to do stuff like this with your ActiveRecord models:
21
+
22
+ m = MyModel.find(12345)
23
+
24
+ m.the_geom
25
+ # => spits out the untouched geometry value as a string in WKB
26
+
27
+ m.the_geom_geos
28
+ # => spits out the geometry wrapped in a Geos::Geometry object
29
+
30
+ m.the_geom = 'POINT(0 0)'
31
+ # => setters will automatically make conversions from any of the formats
32
+ # that the Geos.read can recognize, so Google Maps formats, WKT, WKB,
33
+ # etc. are all converted automatically.
34
+
35
+ m.the_geom_wkt
36
+ # => automatically converts to a WKT string
37
+
38
+ m.the_geom_wkb_bin
39
+ # => automatically converts to WKB in binary
40
+
41
+ There's also some funky SRID handling code that will automatically
42
+ look in the geometry_columns table to make conversions for you when
43
+ necessary. Saving WKT as "SRID=default; POINT(0 0)" for instance will
44
+ automatically set the SRID when saving the ActiveRecord, or the SRID
45
+ can be specified manually.
46
+
47
+ * multiple geometry columns are supported and detected for
48
+ automatically. These column accessors are all generated dynamically at
49
+ run time.
50
+
51
+ * automatic generation of named scopes for ActiveRecord models. The
52
+ usual suspects are supported such as +st_contains+, +st_intersects+, etc.
53
+
54
+ * ordering scopes are also included such as +order_by_st_area+,
55
+ +order_by_st_length+, etc.
56
+
57
+ * together, the various spatial scopes let you chain together scopes to build
58
+ geospatial queries:
59
+
60
+ neighbourhood = Neighbourhood.find(12345)
61
+ my_model = MyModel.active.
62
+ recent.
63
+ st_within(neighbourhood.the_geom_geos.envelope).
64
+ st_dwithin(point, 0.1).
65
+ order_by_st_area.
66
+ all(
67
+ :limit => 10
68
+ )
69
+
70
+ * spatial associations via the +has_many_spatially+ association method. This
71
+ method essentially operates like a standard +has_many+ association with
72
+ the added ability to define a spatial relationship with the +:relationship+
73
+ option rather than relying on direct equality. This allows you to set up
74
+ relationships like the following:
75
+
76
+ class Neighbourhood < ActiveRecord::Base
77
+ has_many_spatially :cities,
78
+ :relationship => :contains
79
+ end
80
+
81
+ class City < ActiveRecord::Base
82
+ has_many_spatially :neighbourhoods,
83
+ :relationship => :within
84
+ end
85
+
86
+ Neighbourhood.first.cities
87
+ #=> All cities that the neighbourhood is within
88
+
89
+ City.first.neighbourhoods
90
+ #=> All neighbourhoods contained by the city
91
+
92
+ City.includes(:neighbourhoods).first.neighbourhoods
93
+ #=> Eager loading works too
94
+
95
+
96
+ See the documentation for ActiveRecordSpatial::Associations for details.
97
+
98
+ == Running ActiveRecord Tests
99
+
100
+ The test helper attempts to install PostGIS on the test database, but in the
101
+ event that it cannot find your PostGIS files you can either help to guide
102
+ the PostGIS detection by setting a +POSTGIS_PATH+ environment variable that
103
+ points to your +postgis.sql+ and +spatial_ref_sys.sql+ files.
104
+
105
+ You can set up local database settings in the test/local_database.yml file.
106
+ See the test/database.yml file for example settings.
107
+
108
+ == ActiveRecord Versions Supported
109
+
110
+ We're starting to standardize on Rails 3+ for our purposes, so future versions
111
+ this gem function on versions of Rails 3 and above. To use spatial scopes on
112
+ previous versions of ActiveRecord, see our geos-extensions gem available at
113
+ https://github.com/zoocasa/geos-extensions . We have pulled the ActiveRecord
114
+ extensions out of that gem and are instead packaging them here, thus allowing
115
+ you to use the actual Geos extensions in an unfettered way. If you wish to
116
+ use the spatial extensions with versions of Rails prior to 3, see versions of
117
+ the geos-extensions gem prior to version 0.3.0 where the split occured.
118
+
119
+ == PostGIS and PostgreSQL Versions Supported
120
+
121
+ As of this writing, things look good for PostgreSQL 9.1 and 9.2 for both
122
+ PostGIS 1.5 and 2.0. Some features are only available in PostGIS 2.0, such
123
+ as the +st_3dintersects+ scope and the like, as that spatial relationship
124
+ function was added in PostGIS 2.0.
125
+
126
+ PostgreSQL 9.0 and below are currently not supported as some of the SQL we
127
+ produce to create the spatial queries is not supported in older PostgreSQL
128
+ versions.
129
+
130
+ In any event, it is recommended that you use current versions of both
131
+ PostgreSQL and PostGIS where possible.
132
+
133
+ == Migrating from geos-extensions
134
+
135
+ There are a few changes with ActiveRecord Spatial that aren't backwards
136
+ compatible with older versions of geos-extensions. The migration path is
137
+ pretty easy all the same and should only require a handful of changes for
138
+ most projects.
139
+
140
+ * The Geos::ActiveRecord module has been renamed to ActiveRecordSpatial.
141
+ This means you'll have to include ActiveRecordSpatial::SpatialColumns into
142
+ your models instead of Geos::ActiveRecord::SpatialColumns, for instance.
143
+
144
+ * ActiveRecordSpatial::SpatialScopes is no longer automatically included
145
+ when ActiveRecordSpatial::SpatialColumns is included. This allows you to
146
+ optionally include SpatialScopes, as sometimes folks just don't want to use
147
+ our spatial scopes.
148
+
149
+ * In an effort to move away from PostgreSQL/PostGIS-specifics (even if we're
150
+ not fully there yet), we've removed the PostgreSQLSpatialColumn class and
151
+ made everything use ActiveRecordSpatial::GeometryColumn and
152
+ ActiveRecordSpatial::GeographyColumn ActiveRecord models directly. These
153
+ are full-fledged ActiveRecord models now and can be accessed as such, but
154
+ they should be considered read-only models (and are marked as such). In
155
+ PostGIS 2.0+, for instance, these are both read-only views.
156
+
157
+ * The pseudo constant methods for UNKNOWN_SRID and POSTGIS and such are now
158
+ real constants under ActiveRecordSpatial.
159
+
160
+ * The +order_by_*+ scopes have all been renamed to +order_by_st_*+ to better
161
+ reflect that they are calling ST functions, to improve consistency with
162
+ the relationship scopes, and to lessen potential name clashes with other
163
+ existing scopes that may be named with similar conventions.
164
+
165
+ == License
166
+
167
+ This gem is licensed under an MIT-style license. See the +MIT-LICENSE+ file for
168
+ details.
169
+
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+
2
+ # -*- ruby -*-
3
+
4
+ require 'rubygems'
5
+ require 'rubygems/package_task'
6
+ require 'rake/testtask'
7
+ require 'rdoc/task'
8
+ require 'bundler/gem_tasks'
9
+
10
+ $:.push File.expand_path(File.dirname(__FILE__), 'lib')
11
+
12
+ version = ActiveRecordSpatial::VERSION
13
+
14
+ desc 'Test ActiveRecord Spatial'
15
+ Rake::TestTask.new(:test) do |t|
16
+ t.test_files = FileList['test/**/*_tests.rb']
17
+ t.verbose = !!ENV['VERBOSE_TESTS']
18
+ t.warning = !!ENV['WARNINGS']
19
+ end
20
+
21
+ desc 'Build docs'
22
+ Rake::RDocTask.new do |t|
23
+ t.main = 'README.rdoc'
24
+ t.title = "ActiveRecord Spatial #{version}"
25
+ t.rdoc_dir = 'doc'
26
+ t.rdoc_files.include('README.rdoc', 'MIT-LICENSE', 'lib/**/*.rb')
27
+ end
28
+
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require File.expand_path('../lib/activerecord-spatial/version', __FILE__)
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "activerecord-spatial"
7
+ s.version = ActiveRecordSpatial::VERSION
8
+
9
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
10
+ s.authors = ["J Smith"]
11
+ s.description = "ActiveRecord Spatial gives AR the ability to work with PostGIS columns."
12
+ s.summary = s.description
13
+ s.email = "code@zoocasa.com"
14
+ s.extra_rdoc_files = [
15
+ "README.rdoc"
16
+ ]
17
+ s.files = `git ls-files`.split($\)
18
+ s.executables = s.files.grep(%r{^bin/}).map { |f| File.basename(f) }
19
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
20
+ s.homepage = "https://github.com/zoocasa/activerecord-spatial"
21
+ s.require_paths = ["lib"]
22
+
23
+ s.add_dependency("rails", [">= 3.2"])
24
+ s.add_dependency("geos-extensions", [">= 0.3.0.dev"])
25
+ end
26
+
@@ -0,0 +1,32 @@
1
+
2
+ require 'geos-extensions'
3
+
4
+ require 'activerecord-spatial/active_record/connection_adapters/postgresql/adapter_extensions'
5
+
6
+ module ActiveRecordSpatial
7
+ BASE_PATH = File.dirname(__FILE__)
8
+
9
+ class << self
10
+ def geometry_columns?
11
+ ::ActiveRecord::Base.connection.geometry_columns?
12
+ end
13
+
14
+ def geography_columns?
15
+ ::ActiveRecord::Base.connection.geography_columns?
16
+ end
17
+
18
+ def default_column_name
19
+ @default_column_name ||= :the_geom
20
+ end
21
+
22
+ # Allows you to modify the default geometry column name for all of
23
+ # ActiveRecordSpatial. This is useful when you have a common column name
24
+ # for all of your geometry columns, such as +wkb+, +feature+, +geom+, etc.
25
+ def default_column_name=(column_name)
26
+ @default_column_name = column_name
27
+ end
28
+ end
29
+ end
30
+
31
+ require 'activerecord-spatial/active_record'
32
+
@@ -0,0 +1,14 @@
1
+
2
+ module ActiveRecordSpatial
3
+ autoload :SpatialFunction, 'activerecord-spatial/spatial_function'
4
+ autoload :SpatialColumns, 'activerecord-spatial/spatial_columns'
5
+ autoload :SpatialScopeConstants, 'activerecord-spatial/spatial_scope_constants'
6
+ autoload :SpatialScopes, 'activerecord-spatial/spatial_scopes'
7
+ autoload :SpatialColumn, 'activerecord-spatial/active_record/models/spatial_column'
8
+ autoload :SpatialRefSys, 'activerecord-spatial/active_record/models/spatial_ref_sys'
9
+ autoload :GeometryColumn, 'activerecord-spatial/active_record/models/geometry_column'
10
+ autoload :GeographyColumn, 'activerecord-spatial/active_record/models/geography_column'
11
+ end
12
+
13
+ require 'activerecord-spatial/associations'
14
+
@@ -0,0 +1,36 @@
1
+
2
+ require 'active_record/connection_adapters/postgresql_adapter'
3
+
4
+ module ActiveRecord
5
+ module ConnectionAdapters
6
+ class PostgreSQLColumn
7
+ def simplified_type_with_spatial_type(field_type)
8
+ if field_type =~ /^geometry(\(|$)/
9
+ :geometry
10
+ elsif field_type =~ /^geography(\(|$)/
11
+ :geography
12
+ else
13
+ simplified_type_without_spatial_type(field_type)
14
+ end
15
+ end
16
+ alias_method_chain :simplified_type, :spatial_type
17
+ end
18
+
19
+ class PostgreSQLAdapter
20
+ def geometry_columns?
21
+ true
22
+ end
23
+
24
+ def geography_columns?
25
+ ActiveRecordSpatial::POSTGIS[:lib] >= '1.5'
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ module ActiveRecordSpatial
32
+ autoload :POSTGIS, 'activerecord-spatial/active_record/connection_adapters/postgresql/postgis'
33
+ autoload :UNKNOWN_SRID, 'activerecord-spatial/active_record/connection_adapters/postgresql/unknown_srid'
34
+ autoload :UNKNOWN_SRIDS, 'activerecord-spatial/active_record/connection_adapters/postgresql/unknown_srid'
35
+ end
36
+
@@ -0,0 +1,24 @@
1
+
2
+ module ActiveRecordSpatial
3
+ POSTGIS = begin
4
+ if (version_string = ::ActiveRecord::Base.connection.select_rows("SELECT postgis_full_version()").flatten.first).present?
5
+ hash = {
6
+ :use_stats => version_string =~ /USE_STATS/
7
+ }
8
+
9
+ {
10
+ :lib => /POSTGIS="([^"]+)"/,
11
+ :geos => /GEOS="([^"]+)"/,
12
+ :proj => /PROJ="([^"]+)"/,
13
+ :libxml => /LIBXML="([^"]+)"/
14
+ }.each do |k, v|
15
+ hash[k] = version_string.scan(v).flatten.first
16
+ end
17
+
18
+ hash.freeze
19
+ else
20
+ {}.freeze
21
+ end
22
+ end
23
+ end
24
+
@@ -0,0 +1,21 @@
1
+
2
+ module ActiveRecordSpatial
3
+ UNKNOWN_SRIDS = begin
4
+ if ActiveRecordSpatial::POSTGIS[:lib] >= '2.0'
5
+ {
6
+ :geography => 0,
7
+ :geometry => 0
8
+ }.freeze
9
+ else
10
+ {
11
+ :geography => 0,
12
+ :geometry => -1
13
+ }.freeze
14
+ end
15
+ end
16
+
17
+ UNKNOWN_SRID = begin
18
+ ActiveRecordSpatial::UNKNOWN_SRIDS[:geometry]
19
+ end
20
+ end
21
+