geo_foo 0.0.1
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/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +13 -0
- data/README.rdoc +29 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/geo_foo.gemspec +56 -0
- data/lib/geo_foo/active_record.rb +29 -0
- data/lib/geo_foo/core.rb +70 -0
- data/lib/geo_foo/numeric.rb +8 -0
- data/lib/geo_foo/scope.rb +9 -0
- data/lib/geo_foo.rb +15 -0
- data/test/helper.rb +31 -0
- data/test/test_geo_foo.rb +25 -0
- data/test_database.yml +3 -0
- metadata +72 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
THE BEER-WARE LICENSE Copyright (c) 2010 hukl, agnat
|
2
|
+
|
3
|
+
As long as you retain this notice you can do whatever you want with this stuff.
|
4
|
+
If we meet some day, and you think this stuff is worth it, you can buy us a beer
|
5
|
+
in return.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
8
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
9
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
10
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
11
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
12
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
13
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
= geo_foo
|
2
|
+
|
3
|
+
Run
|
4
|
+
|
5
|
+
rake build
|
6
|
+
rake install
|
7
|
+
|
8
|
+
= Running the tests
|
9
|
+
To run the tests you need a postgresql server with postgis extensions installed.
|
10
|
+
|
11
|
+
* create an empty database +geo_foo_test+ from your postgis template
|
12
|
+
* set your settings in <tt>test_database.yml</tt>.
|
13
|
+
|
14
|
+
Now run:
|
15
|
+
rake [test]
|
16
|
+
|
17
|
+
== Note on Patches/Pull Requests
|
18
|
+
|
19
|
+
* Fork the project.
|
20
|
+
* Make your feature addition or bug fix.
|
21
|
+
* Add tests for it. This is important so I don't break it in a
|
22
|
+
future version unintentionally.
|
23
|
+
* Commit, do not mess with rakefile, version, or history.
|
24
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
25
|
+
* Send me a pull request. Bonus points for topic branches.
|
26
|
+
|
27
|
+
== Copyright
|
28
|
+
|
29
|
+
Copyright (c) 2010 hukl, agnat. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'jeweler'
|
7
|
+
Jeweler::Tasks.new do |gem|
|
8
|
+
gem.name = "geo_foo"
|
9
|
+
gem.summary = %Q{An experimental and extensible Rails/Postgis library}
|
10
|
+
gem.description = %Q{Geo makes it easy to interact with Postgis without hacking too deep into AR. It is in a very early stage and should be considered experimental at most}
|
11
|
+
gem.email = "contact@smyck.org"
|
12
|
+
gem.homepage = "http://github.com/hukl/geo_foo"
|
13
|
+
gem.authors = ["agnat", "hukl"]
|
14
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
|
+
end
|
16
|
+
Jeweler::GemcutterTasks.new
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'rake/testtask'
|
22
|
+
Rake::TestTask.new(:test) do |test|
|
23
|
+
test.libs << 'lib' << 'test'
|
24
|
+
test.pattern = 'test/**/test_*.rb'
|
25
|
+
test.verbose = true
|
26
|
+
end
|
27
|
+
|
28
|
+
begin
|
29
|
+
require 'rcov/rcovtask'
|
30
|
+
Rcov::RcovTask.new do |test|
|
31
|
+
test.libs << 'test'
|
32
|
+
test.pattern = 'test/**/test_*.rb'
|
33
|
+
test.verbose = true
|
34
|
+
end
|
35
|
+
rescue LoadError
|
36
|
+
task :rcov do
|
37
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
task :test => :check_dependencies
|
42
|
+
|
43
|
+
task :default => :test
|
44
|
+
|
45
|
+
require 'rake/rdoctask'
|
46
|
+
Rake::RDocTask.new do |rdoc|
|
47
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
48
|
+
|
49
|
+
rdoc.rdoc_dir = 'rdoc'
|
50
|
+
rdoc.title = "geo_foo #{version}"
|
51
|
+
rdoc.rdoc_files.include('README*')
|
52
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
53
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/geo_foo.gemspec
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{geo_foo}
|
8
|
+
s.version = "0.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["agnat", "hukl"]
|
12
|
+
s.date = %q{2010-03-12}
|
13
|
+
s.description = %q{Geo makes it easy to interact with Postgis without hacking too deep into AR. It is in a very early stage and should be considered experimental at most}
|
14
|
+
s.email = %q{contact@smyck.org}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"geo_foo.gemspec",
|
27
|
+
"lib/geo_foo.rb",
|
28
|
+
"lib/geo_foo/active_record.rb",
|
29
|
+
"lib/geo_foo/core.rb",
|
30
|
+
"lib/geo_foo/numeric.rb",
|
31
|
+
"lib/geo_foo/scope.rb",
|
32
|
+
"test/helper.rb",
|
33
|
+
"test/test_geo_foo.rb",
|
34
|
+
"test_database.yml"
|
35
|
+
]
|
36
|
+
s.homepage = %q{http://github.com/hukl/geo_foo}
|
37
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
38
|
+
s.require_paths = ["lib"]
|
39
|
+
s.rubygems_version = %q{1.3.5}
|
40
|
+
s.summary = %q{An experimental and extensible Rails/Postgis library}
|
41
|
+
s.test_files = [
|
42
|
+
"test/helper.rb",
|
43
|
+
"test/test_geo_foo.rb"
|
44
|
+
]
|
45
|
+
|
46
|
+
if s.respond_to? :specification_version then
|
47
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
48
|
+
s.specification_version = 3
|
49
|
+
|
50
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
51
|
+
else
|
52
|
+
end
|
53
|
+
else
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module GeoFoo
|
2
|
+
module ActiveRecord
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def add_geo_foo
|
10
|
+
self.class_eval do
|
11
|
+
def self.within_radius lat, lon, radius
|
12
|
+
scoped(
|
13
|
+
:conditions => [
|
14
|
+
"ST_DWithin(#{GeoFoo::Core.as_point(lat,lon)}, "\
|
15
|
+
"point, #{bbox_size(lat, radius)}) AND ST_Distance_Sphere(" \
|
16
|
+
"point, #{GeoFoo::Core.as_point(lat,lon)}) < #{radius}"
|
17
|
+
]
|
18
|
+
)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# XXX handle case where lat is (close to) +-90deg (poles)
|
24
|
+
def bbox_size latitude, radius
|
25
|
+
(radius.to_f / (GeoFoo::Core::EarthRadius * Math.cos(latitude.to_rad))).to_deg
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
data/lib/geo_foo/core.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
module GeoFoo
|
2
|
+
|
3
|
+
module Core
|
4
|
+
SRID = 4326 # WGS-84
|
5
|
+
EarthRadius = 6370986.0 # meters (as used by postgis' ST_Distance_Sphere())
|
6
|
+
TableName = "locations"
|
7
|
+
|
8
|
+
# execute an SQL query on the database
|
9
|
+
def self.execute query
|
10
|
+
ActiveRecord::Base.connection.execute( query ).to_a
|
11
|
+
end
|
12
|
+
|
13
|
+
# return a postgis string representation of the given coordinates
|
14
|
+
def self.as_point lat, lon
|
15
|
+
# Intentionally use (lat,lon) and not (lon,lat), because latitude is the
|
16
|
+
# 'horizontal' coordinate.
|
17
|
+
# See: http://archives.postgresql.org/pgsql-general/2008-02/msg01393.php
|
18
|
+
"ST_GeomFromText('POINT(#{lon} #{lat})', #{SRID})"
|
19
|
+
end
|
20
|
+
|
21
|
+
# find all locations within a radius for a given location
|
22
|
+
def self.find_neighbours_by_coords lat, lon, radius=100.0
|
23
|
+
# compute an appropriate bounding box size for this latitude
|
24
|
+
# XXX handle case where lat is (close to) +-90deg (poles)
|
25
|
+
bbox_size= (radius.to_f / (EarthRadius * Math.cos(lat.to_rad))).to_deg
|
26
|
+
distance = "ST_Distance_Sphere(point, #{as_point(lat,lon)})"
|
27
|
+
(execute "SELECT (id) FROM #{TableName} "\
|
28
|
+
"WHERE ST_DWithin(#{as_point(lat,lon)}, point, #{bbox_size}) "\
|
29
|
+
"AND #{distance} < #{radius} "\
|
30
|
+
"ORDER BY #{distance}").map { |row| row["id"].to_i }
|
31
|
+
end
|
32
|
+
|
33
|
+
# return latitude, longitude for a given id
|
34
|
+
def self.find_coords_by_id id
|
35
|
+
r = (execute "SELECT ST_Y(point), ST_X(point) FROM #{TableName} "\
|
36
|
+
"WHERE id = #{id}")[0]
|
37
|
+
# Intentionally return (y,x) which corresponds to (lat,lon). See as_point().
|
38
|
+
[r["st_y"].to_f, r["st_x"].to_f]
|
39
|
+
end
|
40
|
+
|
41
|
+
# store a point in the location table. returns the points id.
|
42
|
+
def self.store_location lat, lon
|
43
|
+
(execute "INSERT INTO #{TableName} (point) VALUES (#{as_point(lat,lon)})"\
|
44
|
+
"RETURNING id").first["id"].to_i
|
45
|
+
end
|
46
|
+
|
47
|
+
# delete a location from the database
|
48
|
+
def self.delete_location id
|
49
|
+
execute "DELETE FROM #{TableName} WHERE id = #{id}"
|
50
|
+
end
|
51
|
+
|
52
|
+
# returns the number of locations currently in the database
|
53
|
+
def self.location_count
|
54
|
+
(execute "SELECT count(*) FROM #{TableName}").first["count"].to_i
|
55
|
+
end
|
56
|
+
|
57
|
+
# migration helper: create the database table
|
58
|
+
def self.create_table
|
59
|
+
execute "CREATE TABLE #{TableName} (id serial PRIMARY KEY)"
|
60
|
+
execute "SELECT AddGeometryColumn('#{TableName}', 'point', #{SRID}, 'POINT', 2)"
|
61
|
+
execute "CREATE INDEX #{TableName}_point_index ON #{TableName} USING GIST (point)"
|
62
|
+
end
|
63
|
+
|
64
|
+
# migration helper: drop the database table
|
65
|
+
def self.drop_table
|
66
|
+
execute "DROP TABLE #{TableName}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
data/lib/geo_foo.rb
ADDED
data/test/helper.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'active_record'
|
4
|
+
require 'active_support'
|
5
|
+
require 'active_support/test_case'
|
6
|
+
|
7
|
+
config_file = File.join(File.dirname(__FILE__), '..', 'test_database.yml')
|
8
|
+
database_config = YAML.load_file config_file
|
9
|
+
database_config[:adapter] = 'postgresql'
|
10
|
+
ActiveRecord::Base.establish_connection database_config
|
11
|
+
|
12
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
13
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
14
|
+
require 'geo_foo'
|
15
|
+
|
16
|
+
class ActiveSupport::TestCase
|
17
|
+
|
18
|
+
def execute sql
|
19
|
+
ActiveRecord::Base.connection.execute sql
|
20
|
+
end
|
21
|
+
|
22
|
+
def query sql
|
23
|
+
ActiveRecord::Base.connection.query sql
|
24
|
+
end
|
25
|
+
|
26
|
+
def query_scalar sql
|
27
|
+
query(sql).first.first
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'geo_foo/core'
|
2
|
+
require 'helper'
|
3
|
+
|
4
|
+
class TestGeoFoo < ActiveSupport::TestCase
|
5
|
+
|
6
|
+
test "database connection" do
|
7
|
+
# perform a query first to 'wake-up' the connection
|
8
|
+
assert_equal(query_scalar("SELECT 5").to_i, 5)
|
9
|
+
assert ActiveRecord::Base.connected?, "database connection esteblished"
|
10
|
+
end
|
11
|
+
|
12
|
+
test "postgis database is present" do
|
13
|
+
assert(query('SELECT count(*) FROM geometry_columns'))
|
14
|
+
end
|
15
|
+
|
16
|
+
test "as_point" do
|
17
|
+
latitude = 5
|
18
|
+
longitude = 42
|
19
|
+
point = GeoFoo::Core.as_point latitude, longitude
|
20
|
+
assert(query("SELECT #{point}"))
|
21
|
+
assert_equal(query_scalar("SELECT ST_X(#{point})").to_i, 42)
|
22
|
+
assert_equal(query_scalar("SELECT ST_Y(#{point})").to_i, 5)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
data/test_database.yml
ADDED
metadata
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: geo_foo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- agnat
|
8
|
+
- hukl
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2010-03-12 00:00:00 +01:00
|
14
|
+
default_executable:
|
15
|
+
dependencies: []
|
16
|
+
|
17
|
+
description: Geo makes it easy to interact with Postgis without hacking too deep into AR. It is in a very early stage and should be considered experimental at most
|
18
|
+
email: contact@smyck.org
|
19
|
+
executables: []
|
20
|
+
|
21
|
+
extensions: []
|
22
|
+
|
23
|
+
extra_rdoc_files:
|
24
|
+
- LICENSE
|
25
|
+
- README.rdoc
|
26
|
+
files:
|
27
|
+
- .document
|
28
|
+
- .gitignore
|
29
|
+
- LICENSE
|
30
|
+
- README.rdoc
|
31
|
+
- Rakefile
|
32
|
+
- VERSION
|
33
|
+
- geo_foo.gemspec
|
34
|
+
- lib/geo_foo.rb
|
35
|
+
- lib/geo_foo/active_record.rb
|
36
|
+
- lib/geo_foo/core.rb
|
37
|
+
- lib/geo_foo/numeric.rb
|
38
|
+
- lib/geo_foo/scope.rb
|
39
|
+
- test/helper.rb
|
40
|
+
- test/test_geo_foo.rb
|
41
|
+
- test_database.yml
|
42
|
+
has_rdoc: true
|
43
|
+
homepage: http://github.com/hukl/geo_foo
|
44
|
+
licenses: []
|
45
|
+
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options:
|
48
|
+
- --charset=UTF-8
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
version:
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "0"
|
62
|
+
version:
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.3.5
|
67
|
+
signing_key:
|
68
|
+
specification_version: 3
|
69
|
+
summary: An experimental and extensible Rails/Postgis library
|
70
|
+
test_files:
|
71
|
+
- test/helper.rb
|
72
|
+
- test/test_geo_foo.rb
|