geoweb 0.0.5
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/README +0 -0
- data/lib/geoweb.rb +26 -0
- data/lib/geoweb/coordinate.rb +29 -0
- data/lib/geoweb/lat_lon.rb +124 -0
- data/lib/geoweb/location.rb +49 -0
- data/lib/geoweb/map.rb +98 -0
- data/lib/geoweb/point.rb +98 -0
- data/lib/geoweb/projection.rb +7 -0
- data/lib/geoweb/projection/base.rb +42 -0
- data/lib/geoweb/projection/linear.rb +15 -0
- data/lib/geoweb/projection/mercator.rb +21 -0
- data/lib/geoweb/transformation.rb +59 -0
- data/spec/core/lat_lon_spec.rb +75 -0
- data/spec/spec_helper.rb +11 -0
- metadata +80 -0
data/README
ADDED
File without changes
|
data/lib/geoweb.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
#require 'matrix'
|
2
|
+
#require 'mathn'
|
3
|
+
#require 'bigdecimal'
|
4
|
+
#TODO - Procurar Proj4js e Procurar Proj4R(uby)
|
5
|
+
|
6
|
+
module GeoWeb
|
7
|
+
DEG_TO_RAD = Math::PI / 180.0
|
8
|
+
|
9
|
+
autoload :Point, 'geoweb/point'
|
10
|
+
autoload :Location, 'geoweb/location'
|
11
|
+
autoload :LatLon, 'geoweb/lat_lon'
|
12
|
+
autoload :Coordinate, 'geoweb/coordinate'
|
13
|
+
autoload :Transformation, 'geoweb/transformation'
|
14
|
+
autoload :Projection, 'geoweb/projection'
|
15
|
+
autoload :Map, 'geoweb/map'
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def deg_to_rad(location)
|
19
|
+
location * [DEG_TO_RAD, DEG_TO_RAD, 1]
|
20
|
+
end
|
21
|
+
|
22
|
+
def rad_to_deg(location)
|
23
|
+
location / [DEG_TO_RAD, DEG_TO_RAD, 1]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module GeoWeb
|
3
|
+
class Coordinate < Point
|
4
|
+
MAX_ZOOM = 25
|
5
|
+
|
6
|
+
alias row y
|
7
|
+
alias row= y=
|
8
|
+
|
9
|
+
alias column x
|
10
|
+
alias column= x=
|
11
|
+
|
12
|
+
def zoom_to(destination)
|
13
|
+
self.class.new(self.x * 2 ** (destination - self.z),
|
14
|
+
self.y * 2 ** (destination - self.z),
|
15
|
+
destination)
|
16
|
+
end
|
17
|
+
|
18
|
+
def zoom_by(distance)
|
19
|
+
self.class.new(self.x * 2 ** distance,
|
20
|
+
self.y * 2 ** distance,
|
21
|
+
self.z + distance)
|
22
|
+
end
|
23
|
+
|
24
|
+
# def to_s
|
25
|
+
# "(#{sprintf('%f', @x)}, #{sprintf('%f', @y)}, #{@z})"
|
26
|
+
# end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module GeoWeb
|
3
|
+
class LatLon
|
4
|
+
attr_accessor :lat, :lon
|
5
|
+
alias y lat
|
6
|
+
alias y= lat=
|
7
|
+
alias x lon
|
8
|
+
alias x= lon=
|
9
|
+
|
10
|
+
#If the object respond only to lat and lon, the second parameter can be z
|
11
|
+
#Location.new(lat, lon)
|
12
|
+
#Location.new(obj) - obj.lat and obj.lon
|
13
|
+
#Location.new(obj) - obj.y and obj.x
|
14
|
+
#Location.new(hash) - hash[:lat] and hash[:lon]
|
15
|
+
#Location.new(array) - hash[:lat] and hash[:lon]
|
16
|
+
def initialize(lat_or_object, lon=nil)
|
17
|
+
|
18
|
+
lat = lat_or_object
|
19
|
+
if lon.nil?
|
20
|
+
object = lat_or_object
|
21
|
+
lat, lon = if latlongeable?(object)
|
22
|
+
[object.lat, object.lon]
|
23
|
+
elsif object.respond_to?(:y) and object.respond_to?(:x)
|
24
|
+
[object.y, object.x]
|
25
|
+
elsif object.respond_to?(:to_a)
|
26
|
+
object.to_a.values_at 0, 1
|
27
|
+
elsif object.respond_to?(:to_hash)
|
28
|
+
object.to_hash.values_at :lat, :lon
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
@lat, @lon = lat, lon
|
33
|
+
end
|
34
|
+
|
35
|
+
def +(other)
|
36
|
+
other = self.class.new(other) unless latlongeable?(other)
|
37
|
+
self.class.new(@lat + other.lat, @lon + other.lon)
|
38
|
+
end
|
39
|
+
|
40
|
+
def -(other)
|
41
|
+
other = self.class.new(other) unless latlongeable?(other)
|
42
|
+
self.class.new(@lat - other.lat, @lon - other.lon)
|
43
|
+
end
|
44
|
+
|
45
|
+
def ==(other)
|
46
|
+
other = self.class.new(other) unless latlongeable?(other)
|
47
|
+
@lat == other.lat and @lon == other.lon
|
48
|
+
end
|
49
|
+
|
50
|
+
# LatLon multiplication. Accepts other LatLon, a Numeric
|
51
|
+
def *(other)
|
52
|
+
if other.kind_of? Numeric
|
53
|
+
self.class.new(@lat.to_f * other.to_f, @lon.to_f * other.to_f)
|
54
|
+
else
|
55
|
+
other = self.class.new(other) unless latlongeable?(other)
|
56
|
+
self.class.new(@lat.to_f * other.lat.to_f, @lon.to_f * other.lon.to_f)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# LatLon division. Accepts other LatLon, a Numeric or an Array
|
61
|
+
def /(other)
|
62
|
+
if other.kind_of? Numeric
|
63
|
+
self.class.new(@lat.to_f./(other.to_f), @lon.to_f./(other.to_f))
|
64
|
+
else
|
65
|
+
other = self.class.new(other) unless latlongeable?(other)
|
66
|
+
self.class.new(@lat.to_f./(other.lat.to_f), @lon.to_f./(other.lon.to_f))
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def round!
|
71
|
+
@lat = @lat.round
|
72
|
+
@lon = @lon.round
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
|
+
def round
|
77
|
+
self.clone.round!
|
78
|
+
end
|
79
|
+
|
80
|
+
def abs!
|
81
|
+
@lat = @lat.abs
|
82
|
+
@lon = @lon.abs
|
83
|
+
self
|
84
|
+
end
|
85
|
+
|
86
|
+
def abs
|
87
|
+
self.clone.abs!
|
88
|
+
end
|
89
|
+
|
90
|
+
def to_rad
|
91
|
+
self.class.to_rad(self)
|
92
|
+
end
|
93
|
+
|
94
|
+
def to_deg
|
95
|
+
self.class.to_deg(self)
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.to_rad(latlon)
|
99
|
+
latlon * [GeoWeb::DEG_TO_RAD, GeoWeb::DEG_TO_RAD]
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.to_deg(latlon)
|
103
|
+
latlon / [GeoWeb::DEG_TO_RAD, GeoWeb::DEG_TO_RAD]
|
104
|
+
end
|
105
|
+
|
106
|
+
def to_a
|
107
|
+
[@lat, @lon]
|
108
|
+
end
|
109
|
+
|
110
|
+
def to_hash
|
111
|
+
{:lat => @lat, :lon => @lon}
|
112
|
+
end
|
113
|
+
|
114
|
+
def to_s
|
115
|
+
"(#{sprintf('%f', lat)}, #{sprintf('%f', lon)})"
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
def latlongeable?(other)
|
120
|
+
other.respond_to?(:lat) and other.respond_to?(:lon)
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module GeoWeb
|
3
|
+
class Location < Point
|
4
|
+
alias lat y
|
5
|
+
alias lat= y=
|
6
|
+
alias lon x
|
7
|
+
alias lon= x=
|
8
|
+
|
9
|
+
#If the object respond only to lat and lon, the second parameter can be z
|
10
|
+
#Location.new(lat, lon)
|
11
|
+
#Location.new(obj) - obj.lat and obj.lon
|
12
|
+
#Location.new(obj) - obj.y and obj.x
|
13
|
+
#TODO - Location.new(hash) - hash[:lat] and hash[:lon]
|
14
|
+
def initialize(lon_or_object, lat=nil)
|
15
|
+
lon = lon_or_object
|
16
|
+
if lon.respond_to? :lat and lon.respond_to? :lon
|
17
|
+
lon, lat = lon_or_object.lon, lon_or_object.lat
|
18
|
+
z = lon_or_object.respond_to?(:z) && lon_or_object.z || lat
|
19
|
+
end
|
20
|
+
|
21
|
+
super(lon, lat)
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_rad
|
25
|
+
Location.to_rad(self)
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_deg
|
29
|
+
Location.to_deg(self)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.to_rad(location)
|
33
|
+
location * [GeoWeb::DEG_TO_RAD, GeoWeb::DEG_TO_RAD, 1]
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.to_deg(location)
|
37
|
+
location / [GeoWeb::DEG_TO_RAD, GeoWeb::DEG_TO_RAD, 1]
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.from_lat_lon(lat, lon)
|
41
|
+
self.new(lon, lat)
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_s
|
45
|
+
"(#{sprintf('%f', lat)}, #{sprintf('%f', lon)})"
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
data/lib/geoweb/map.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
module GeoWeb
|
2
|
+
|
3
|
+
class Map
|
4
|
+
attr_accessor :projection, :zoom #, :transformation
|
5
|
+
attr_accessor :boundaries
|
6
|
+
attr_accessor :tile_width, :tile_height
|
7
|
+
|
8
|
+
# Calculates the map-size (width or height) given a tile size and specific zoom
|
9
|
+
def self.map_extent(zoom, tile_size=256)
|
10
|
+
2 ** (zoom + Math::log(tile_size)/Math::log(2))
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(options={})
|
14
|
+
@tile_width = options[:tile_width] || 256
|
15
|
+
@tile_height = options[:tile_height] || 256
|
16
|
+
@zoom = options[:zoom] || 1
|
17
|
+
@boundaries = options[:boundaries] || {}
|
18
|
+
|
19
|
+
@origin_tile = options[:origin_tile] || Coordinate.new(0, 0, @zoom)
|
20
|
+
@origin_pixel = options[:origin_pixel] || Point.new(0, 0, @zoom)
|
21
|
+
# @dimensions = Point.new(map_extent(@zoom, @tile_width), map_extent(@zoom, @tile_height))
|
22
|
+
|
23
|
+
# @transformation = options[:transformation] || default_transformation
|
24
|
+
@projection = options[:projection] || default_mercator_projection(options[:transformation])
|
25
|
+
end
|
26
|
+
|
27
|
+
#SAME AS DIMENSIONS, retirar isso
|
28
|
+
def width; @width ||= GeoWeb::Map.map_extent(zoom, tile_width); end
|
29
|
+
def height; @height ||= GeoWeb::Map.map_extent(zoom, tile_height); end
|
30
|
+
|
31
|
+
def relative_point(point)
|
32
|
+
(point - @origin_pixel)
|
33
|
+
end
|
34
|
+
|
35
|
+
def absolute_point(offset)
|
36
|
+
(@origin_pixel + offset)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Return an x, y point on the map image for a given geographical location.
|
40
|
+
def location_point(location)
|
41
|
+
coord = projection.location_coordinate(location).zoom_to(zoom)
|
42
|
+
point = (coord - @origin_tile) * [@tile_width, @tile_height]
|
43
|
+
return absolute_point(point)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Faster, but is accurate only to zoom levels lesser than 17
|
47
|
+
def coordinates_to_point(location)
|
48
|
+
lat, lon = location.lat, location.lon
|
49
|
+
|
50
|
+
#Parece que ele faz isso pra representar como uma fração (sem sinal) de 2Pi
|
51
|
+
#http://en.wikipedia.org/wiki/Longitude#Noting_and_calculating_longitude
|
52
|
+
long_deg = (-180.0 - lon).abs
|
53
|
+
long_ppd = width.to_f / 360.0
|
54
|
+
long_ppdrad = width.to_f / (2.0 * Math::PI)
|
55
|
+
px_x = long_deg * long_ppd
|
56
|
+
|
57
|
+
e = Math::sin( lat * (1.0/180.0 * Math::PI) )
|
58
|
+
e = 0.9999 if e > 0.9999
|
59
|
+
e = -0.9999 if e < -0.9999
|
60
|
+
px_y = (height / 2.0) + 0.5 * Math::log((1+e)/(1-e)) * (-long_ppdrad)
|
61
|
+
|
62
|
+
return absolute_point(Point.new(px_x, px_y, zoom))
|
63
|
+
end
|
64
|
+
|
65
|
+
# Return a geographical location on the map image for a given x, y point.
|
66
|
+
def point_location(point)
|
67
|
+
# origin tile at maximum zoom
|
68
|
+
hizoom_origin_tile = @origin_tile.zoom_to(Coordinate::MAX_ZOOM)
|
69
|
+
|
70
|
+
# distance in tile widths from reference tile to point
|
71
|
+
tiles_distance = relative_point(point) / [@tile_width, @tile_height]
|
72
|
+
|
73
|
+
# distance in rows & columns at maximum zoom
|
74
|
+
distance = Coordinate.new(tiles_distance.x, tiles_distance.y, zoom).zoom_to(Coordinate::MAX_ZOOM)
|
75
|
+
distance.z = 0
|
76
|
+
|
77
|
+
# absolute tile at maximum zoom
|
78
|
+
tile = (hizoom_origin_tile + distance).round!
|
79
|
+
tile = tile.zoom_to(zoom)
|
80
|
+
|
81
|
+
location = projection.coordinate_location(tile)
|
82
|
+
return location
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
def default_mercator_projection(transformation)
|
87
|
+
transformation ||= default_transformation
|
88
|
+
GeoWeb::Projection::Mercator.new(26, transformation)
|
89
|
+
end
|
90
|
+
|
91
|
+
def default_transformation
|
92
|
+
# see: http://modestmaps.mapstraction.com/trac/wiki/TileCoordinateComparisons#TileGeolocations
|
93
|
+
GeoWeb::Transformation.new(1.068070779e7, 0.0, 3.355443185e7, 0.0, -1.068070890e7, 3.355443057e7)
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
data/lib/geoweb/point.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module GeoWeb
|
3
|
+
class Point
|
4
|
+
attr_accessor :x, :y, :z
|
5
|
+
|
6
|
+
# ClassMethods
|
7
|
+
class << self
|
8
|
+
def from_hash(coord)
|
9
|
+
self.new(coord[:x], coord[:y], coord[:z])
|
10
|
+
end
|
11
|
+
|
12
|
+
def from_array(coord)
|
13
|
+
self.new(coord[0], coord[1], coord[2])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
#If the object respond only to x and y, the second parameter can be z
|
18
|
+
#Point.new(x, y)
|
19
|
+
#Point.new(obj) - obj.x and obj.y and obj.z
|
20
|
+
#Point.new(obj, z) - obj.x and obj.y
|
21
|
+
#TODO - Point.new(hash) - hash[:x] and hash[:y] and hash[:z]
|
22
|
+
#TODO - Point.new(hash, z) - hash[:x] and hash[:y]
|
23
|
+
def initialize(x_or_object, y=nil, z=nil)
|
24
|
+
x = x_or_object
|
25
|
+
if x_or_object.respond_to? :x and x_or_object.respond_to? :y
|
26
|
+
x, y = x_or_object.x, x_or_object.y
|
27
|
+
z = x_or_object.respond_to?(:z) && x_or_object.z || y
|
28
|
+
end
|
29
|
+
@x, @y, @z = x, y, z
|
30
|
+
end
|
31
|
+
|
32
|
+
def round!
|
33
|
+
@x = @x.round
|
34
|
+
@y = @y.round
|
35
|
+
@z = @z && @z.round
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
def round
|
40
|
+
self.clone.round!
|
41
|
+
end
|
42
|
+
|
43
|
+
# Spatial sum (sums x, y and z)
|
44
|
+
def +(other)
|
45
|
+
self.class.new(self.x + other.x, self.y + other.y, self.z.to_i + other.z.to_i)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Planar sum (sums x and y)
|
49
|
+
def |(other)
|
50
|
+
# new_z = self.z unless self.z != other.z
|
51
|
+
self.class.new(self.x + other.x, self.y + other.y, self.z)
|
52
|
+
end
|
53
|
+
|
54
|
+
def -(other)
|
55
|
+
self.class.new(self.x - other.x, self.y - other.y, self.z.to_i - other.z.to_i)
|
56
|
+
end
|
57
|
+
|
58
|
+
def ==(other)
|
59
|
+
self.class.x == other.x and self.y == other.y and self.z.to_i == other.z.to_i
|
60
|
+
end
|
61
|
+
|
62
|
+
# Point multiplication. Accepts other point, a Numeric or an Array
|
63
|
+
def *(other)
|
64
|
+
if other.respond_to? :x and other.respond_to? :y and other.respond_to? :z
|
65
|
+
self.class.new(self.x.to_f * other.x.to_f, self.y.to_f * other.y.to_f, self.z.to_f * other.z.to_f)
|
66
|
+
elsif other.respond_to? :to_a
|
67
|
+
self * self.class.from_array(other.to_a)
|
68
|
+
else
|
69
|
+
self.class.new(self.x.to_f * other.to_f, self.y.to_f * other.to_f, self.z.to_f * other.to_f)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Point division. Accepts other point, a Numeric or an Array
|
74
|
+
def /(other)
|
75
|
+
# self.class.new(self.x / num, self.y / num, self.z)
|
76
|
+
if other.respond_to? :x and other.respond_to? :y and other.respond_to? :z
|
77
|
+
self.class.new(self.x.to_f / other.x.to_f, self.y.to_f / other.y.to_f, self.z.to_f / other.z.to_f)
|
78
|
+
elsif other.respond_to? :to_a
|
79
|
+
self / self.class.from_array(other.to_a)
|
80
|
+
else
|
81
|
+
self.class.new(self.x.to_f / other.to_f, self.y.to_f / other.to_f, self.z.to_f / other.to_f)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def to_s
|
86
|
+
"(#{sprintf('%f', @x)}, #{sprintf('%f', @y)}, #{@z})"
|
87
|
+
end
|
88
|
+
|
89
|
+
def to_a
|
90
|
+
[@x, @y, @z]
|
91
|
+
end
|
92
|
+
|
93
|
+
def to_hash
|
94
|
+
{:x => @x, :y => @y, :z => @z}
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module GeoWeb
|
2
|
+
module Projection
|
3
|
+
|
4
|
+
class Base
|
5
|
+
attr_accessor :zoom, :transformation
|
6
|
+
|
7
|
+
def initialize(zoom, t=Transformation.new(1, 0, 0, 0, 1, 0))
|
8
|
+
@zoom = zoom
|
9
|
+
@transformation = t
|
10
|
+
end
|
11
|
+
|
12
|
+
def raw_project(point); nil; end
|
13
|
+
def raw_unproject(point); nil; end
|
14
|
+
|
15
|
+
def project(point)
|
16
|
+
point = self.raw_project(point)
|
17
|
+
point = transformation.transform(point) unless transformation.nil?
|
18
|
+
point
|
19
|
+
end
|
20
|
+
|
21
|
+
def unproject(point)
|
22
|
+
point = transformation.untransform(point) unless transformation.nil?
|
23
|
+
point = self.raw_unproject(point)
|
24
|
+
point
|
25
|
+
end
|
26
|
+
|
27
|
+
def location_coordinate(latlon)
|
28
|
+
point = self.project(latlon.to_rad)
|
29
|
+
Coordinate.new(point.x, point.y, self.zoom)
|
30
|
+
end
|
31
|
+
|
32
|
+
def coordinate_location(coordinate)
|
33
|
+
coordinate = coordinate.zoom_to(self.zoom)
|
34
|
+
point = Point.new(coordinate.x, coordinate.y)
|
35
|
+
point = self.unproject(point)
|
36
|
+
LatLon.new(point).to_deg
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module GeoWeb
|
2
|
+
module Projection
|
3
|
+
|
4
|
+
#Mercator OR CylindricMercator?
|
5
|
+
class Mercator < Base
|
6
|
+
|
7
|
+
# OK
|
8
|
+
def raw_project(point)
|
9
|
+
Point.new(point.x,
|
10
|
+
Math::log(Math::tan(0.25 * Math::PI + 0.5 * point.y)))
|
11
|
+
end
|
12
|
+
|
13
|
+
# OK
|
14
|
+
def raw_unproject(point)
|
15
|
+
Point.new(point.x,
|
16
|
+
2 * Math::atan(Math::E ** point.y) - 0.5 * Math::PI)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
module GeoWeb
|
3
|
+
class Transformation
|
4
|
+
attr_reader :ax, :bx, :cx, :ay, :by, :cy
|
5
|
+
|
6
|
+
def initialize(ax, bx, cx, ay, by, cy)
|
7
|
+
@ax, @bx, @cx, @ay, @by, @cy = ax, bx, cx, ay, by, cy
|
8
|
+
end
|
9
|
+
|
10
|
+
# Transforms a point
|
11
|
+
def transform(point)
|
12
|
+
return Point.new(self.ax * point.x + self.bx * point.y + self.cx,
|
13
|
+
self.ay * point.x + self.by * point.y + self.cy)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Inverse of transform
|
17
|
+
def untransform(point)
|
18
|
+
Point.new((point.x*by - point.y*bx - cx*by + cy*bx) / (ax*by - ay*bx),
|
19
|
+
(point.x*ay - point.y*ax - cx*ay + cy*ax) / (bx*ay - by*ax))
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
"T([#{ax},#{bx},#{cx}][#{ay},#{by},#{cy}])"
|
24
|
+
end
|
25
|
+
|
26
|
+
# Generates a transform based on three pairs of points, a1 -> a2, b1 -> b2, c1 -> c2.
|
27
|
+
def self.deriveTransformation(a1x, a1y, a2x, a2y, b1x, b1y, b2x, b2y, c1x, c1y, c2x, c2y)
|
28
|
+
ax, bx, cx = linearSolution(a1x, a1y, a2x, b1x, b1y, b2x, c1x, c1y, c2x)
|
29
|
+
ay, by, cy = linearSolution(a1x, a1y, a2y, b1x, b1y, b2y, c1x, c1y, c2y)
|
30
|
+
|
31
|
+
self.new(ax, bx, cx, ay, by, cy)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Solves a system of linear equations.
|
35
|
+
#
|
36
|
+
# t1 = (a * r1) + (b + s1) + c
|
37
|
+
# t2 = (a * r2) + (b + s2) + c
|
38
|
+
# t3 = (a * r3) + (b + s3) + c
|
39
|
+
#
|
40
|
+
# r1 - t3 are the known values.
|
41
|
+
# a, b, c are the unknowns to be solved.
|
42
|
+
# returns the a, b, c coefficients.
|
43
|
+
def self.linearSolution(r1, s1, t1, r2, s2, t2, r3, s3, t3)
|
44
|
+
# make them all floats
|
45
|
+
r1, s1, t1, r2, s2, t2, r3, s3, t3 = [r1, s1, t1, r2, s2, t2, r3, s3, t3].map{|n| n.to_f}
|
46
|
+
|
47
|
+
a = (((t2 - t3) * (s1 - s2)) - ((t1 - t2) * (s2 - s3))) \
|
48
|
+
/ (((r2 - r3) * (s1 - s2)) - ((r1 - r2) * (s2 - s3)))
|
49
|
+
|
50
|
+
b = (((t2 - t3) * (r1 - r2)) - ((t1 - t2) * (r2 - r3))) \
|
51
|
+
/ (((s2 - s3) * (r1 - r2)) - ((s1 - s2) * (r2 - r3)))
|
52
|
+
|
53
|
+
c = t1 - (r1 * a) - (s1 * b)
|
54
|
+
|
55
|
+
return a, b, c
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe LatLon do
|
4
|
+
|
5
|
+
describe :initialize do
|
6
|
+
|
7
|
+
before(:all) do
|
8
|
+
@lat = -17.4727215168151
|
9
|
+
@lon = -40.6738941939658
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should accept latitude and longitude" do
|
13
|
+
@latlon = LatLon.new(@lat, @lon)
|
14
|
+
@latlon.lat.should == @lat
|
15
|
+
@latlon.lon.should == @lon
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should accept an object with :lat and :lon methods" do
|
19
|
+
lat_lon_object = double("array input")
|
20
|
+
lat_lon_object.stub(:respond_to?).with(any_args()).and_return(false)
|
21
|
+
|
22
|
+
lat_lon_object.should_receive(:respond_to?).with(:lat).and_return(true)
|
23
|
+
lat_lon_object.should_receive(:respond_to?).with(:lon).and_return(true)
|
24
|
+
|
25
|
+
lat_lon_object.should_receive(:lat).and_return(@lat)
|
26
|
+
lat_lon_object.should_receive(:lon).and_return(@lon)
|
27
|
+
|
28
|
+
@latlon = LatLon.new(lat_lon_object)
|
29
|
+
@latlon.lat.should == @lat
|
30
|
+
@latlon.lon.should == @lon
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should accept an object with :x and :y methods" do
|
34
|
+
xy_object = double("xy object input")
|
35
|
+
xy_object.stub(:respond_to?).with(any_args()).and_return(false)
|
36
|
+
|
37
|
+
xy_object.should_receive(:respond_to?).with(:y).and_return(true)
|
38
|
+
xy_object.should_receive(:respond_to?).with(:x).and_return(true)
|
39
|
+
|
40
|
+
xy_object.should_receive(:y).and_return(@lat)
|
41
|
+
xy_object.should_receive(:x).and_return(@lon)
|
42
|
+
|
43
|
+
@latlon = LatLon.new(xy_object)
|
44
|
+
@latlon.lat.should == @lat
|
45
|
+
@latlon.lon.should == @lon
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should accept an Array with two values" do
|
50
|
+
array_or_object = double("array input")
|
51
|
+
array_or_object.stub(:respond_to?).with(any_args()).and_return(false)
|
52
|
+
|
53
|
+
array_or_object.should_receive(:respond_to?).with(:to_a).and_return(true)
|
54
|
+
array_or_object.should_receive(:to_a).and_return([@lat, @lon])
|
55
|
+
|
56
|
+
@latlon = LatLon.new(array_or_object)
|
57
|
+
@latlon.lat.should == @lat
|
58
|
+
@latlon.lon.should == @lon
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should accept a Hash with :lat and :lon keys" do
|
62
|
+
hash_or_object = double("hash input")
|
63
|
+
hash_or_object.stub(:respond_to?).with(any_args()).and_return(false)
|
64
|
+
|
65
|
+
hash_or_object.should_receive(:respond_to?).with(:to_hash).and_return(true)
|
66
|
+
hash_or_object.should_receive(:to_hash).and_return({:lat => @lat, :lon => @lon})
|
67
|
+
|
68
|
+
@latlon = LatLon.new(hash_or_object)
|
69
|
+
@latlon.lat.should == @lat
|
70
|
+
@latlon.lon.should == @lon
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: geoweb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 21
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 5
|
10
|
+
version: 0.0.5
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Reinaldo de Souza Junior
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-08-16 00:00:00 -03:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description:
|
23
|
+
email: juniorz@gmail.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files:
|
29
|
+
- README
|
30
|
+
files:
|
31
|
+
- lib/geoweb.rb
|
32
|
+
- lib/geoweb/projection.rb
|
33
|
+
- lib/geoweb/lat_lon.rb
|
34
|
+
- lib/geoweb/coordinate.rb
|
35
|
+
- lib/geoweb/projection/linear.rb
|
36
|
+
- lib/geoweb/projection/base.rb
|
37
|
+
- lib/geoweb/projection/mercator.rb
|
38
|
+
- lib/geoweb/location.rb
|
39
|
+
- lib/geoweb/map.rb
|
40
|
+
- lib/geoweb/point.rb
|
41
|
+
- lib/geoweb/transformation.rb
|
42
|
+
- spec/spec_helper.rb
|
43
|
+
- spec/core/lat_lon_spec.rb
|
44
|
+
- README
|
45
|
+
has_rdoc: true
|
46
|
+
homepage: http://reinaldojunior.net
|
47
|
+
licenses: []
|
48
|
+
|
49
|
+
post_install_message:
|
50
|
+
rdoc_options: []
|
51
|
+
|
52
|
+
require_paths:
|
53
|
+
- lib
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 3
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
version: "0"
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
hash: 3
|
69
|
+
segments:
|
70
|
+
- 0
|
71
|
+
version: "0"
|
72
|
+
requirements: []
|
73
|
+
|
74
|
+
rubyforge_project:
|
75
|
+
rubygems_version: 1.3.7
|
76
|
+
signing_key:
|
77
|
+
specification_version: 3
|
78
|
+
summary: GeoWeb is a Ruby Geographic Web Library
|
79
|
+
test_files: []
|
80
|
+
|