geoweb 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|