google_static_maps_helper 1.3.1 → 1.3.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +31 -9
- data/VERSION +1 -1
- data/changelog.txt +5 -0
- data/google_static_maps_helper.gemspec +2 -2
- data/lib/google_static_maps_helper.rb +1 -2
- data/lib/google_static_maps_helper/location.rb +79 -4
- data/spec/location_spec.rb +89 -0
- metadata +2 -2
data/README.rdoc
CHANGED
@@ -11,14 +11,14 @@ Then install this gem
|
|
11
11
|
[sudo] gem install google_static_maps_helper
|
12
12
|
|
13
13
|
|
14
|
-
= The easiest way
|
14
|
+
= The easiest way to generating a Google Static Maps URL
|
15
15
|
|
16
16
|
GoogleStaticMapsHelper.url_for do
|
17
17
|
marker :lng => 1, :lat => 2
|
18
18
|
marker :lng => 3, :lat => 4
|
19
19
|
end
|
20
20
|
|
21
|
-
This will return the
|
21
|
+
This will return the URL for a map with these markers. Please note that if you do use this approach you have to set key, size and sensor setting.
|
22
22
|
You do this by:
|
23
23
|
GoogleStaticMapsHelper.key = 'your google key'
|
24
24
|
GoogleStaticMapsHelper.size = '300x600'
|
@@ -31,6 +31,7 @@ If you are on Rails you can put it in your environment configuration file and wh
|
|
31
31
|
end
|
32
32
|
|
33
33
|
It is also possible to create paths:
|
34
|
+
|
34
35
|
point1 = {:lng => 1, :lat => 2}
|
35
36
|
point2 = {:lng => 3, :lat => 4}
|
36
37
|
|
@@ -40,16 +41,18 @@ It is also possible to create paths:
|
|
40
41
|
|
41
42
|
..and of course you can add paths and markers in the same map.
|
42
43
|
|
44
|
+
|
43
45
|
= A bit more "low-level" approach instantiating objects yourself
|
44
46
|
|
47
|
+
If you need a more flexibility you can create objects yourself. Read on to get a quick introduction of the classes in this library.
|
48
|
+
|
45
49
|
== Marker
|
46
|
-
A marker object is, not surprisingly, representing a marker in the Google static map. It is pushed
|
50
|
+
A marker object is, not surprisingly, representing a marker in the Google static map. It is pushed on to the Map object and used
|
47
51
|
to generate the URL for the static map. Markers which are of the same "type" (same color, label and size) are grouped together in the generated URL
|
48
|
-
so that we obey the schema of URL which Google has defined as "markers=markerStyles|markerLocation1|markerLocation2|... etc."
|
52
|
+
so that we obey the schema of URL which Google has defined as "markers=markerStyles|markerLocation1|markerLocation2|... etc.".
|
49
53
|
|
50
54
|
=== How to build a Marker?
|
51
|
-
|
52
|
-
by sending lng and lat in as a hash.
|
55
|
+
You uild a marker by simply sending in an object which responds to lng and lat, or by sending a Hash which has lng and lat keys.
|
53
56
|
marker = GoogleStaticMapsHelper::Marker.new(location)
|
54
57
|
marker = GoogleStaticMapsHelper::Marker.new(:lng => 1, :lat => 2)
|
55
58
|
|
@@ -61,9 +64,9 @@ parameter to the new method if you gave a location object, or include it in the
|
|
61
64
|
|
62
65
|
|
63
66
|
== Map
|
64
|
-
A Map holds many markers, pluss some additional options like the size of the static
|
65
|
-
etc. If no zoom or center point is give it will calculate the map view based on the markers. You can leave markers out, but then you have to supply
|
66
|
-
zoom and center. More info can be found here: http://code.google.com/apis/maps/documentation/staticmaps/#URL_Parameters
|
67
|
+
A Map holds many markers, paths, pluss some additional options like the size of the static map, Google key, what zoom level and center point it should be fixed to
|
68
|
+
etc. If no zoom or center point is give it will calculate the map view based on the markers or paths. You can leave markers and paths out, but then you have to supply
|
69
|
+
zoom and center. More info can be found here: http://code.google.com/apis/maps/documentation/staticmaps/#URL_Parameters.
|
67
70
|
|
68
71
|
=== How to build a map?
|
69
72
|
When building a map object you have to supply key, sensor and size. Other options are optional.
|
@@ -114,6 +117,25 @@ If you feel like it, you can add points at construction time:
|
|
114
117
|
# ..or
|
115
118
|
GoogleStaticMapsHelper::Path.new(point1, point2, :color => :red)
|
116
119
|
|
120
|
+
|
121
|
+
== Locations
|
122
|
+
The location class is what we wrap all our lat- and lng values in. Either it is a Marker, or points for a Path. It has a few helper methods which
|
123
|
+
you can use to calculate distance between locations (<tt>distance_to(another_location)</tt>) and use to generate new locations based on
|
124
|
+
distance and heading (<tt>endpoint(distance, heading)</tt>). It also have a helper method called <tt>endpoints_for_circle_with_radius</tt>
|
125
|
+
which returns locations that make up a circle around the receiving location. Some examples:
|
126
|
+
|
127
|
+
# Create a marker object, which it's lng and lat are stored under the hood as a location object.
|
128
|
+
# Methods which the marker itself cannot answer, like lat and lng and distance_to is delegated to the location object
|
129
|
+
marker = GoogleStaticMapsHelper::Marker(:lng => 1, :lat => 2)
|
130
|
+
|
131
|
+
map << marker
|
132
|
+
|
133
|
+
# Lets say we want draw a circle around our marker, with a radius of 1 kilometer.
|
134
|
+
path = GoogleStaticMapsHelper::Path.new(:fillcolor => :blue, :points => marker.endpoints_for_circle_with_radius(1000))
|
135
|
+
|
136
|
+
map << path
|
137
|
+
|
138
|
+
|
117
139
|
== TODO
|
118
140
|
* Ruby 1.9 support
|
119
141
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.3.
|
1
|
+
1.3.2
|
data/changelog.txt
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
= v.1.3.2 (in git)
|
2
|
+
* Location will now round and reduce numbers so it doesn't get above a precision of 6.
|
3
|
+
* Added two helper methods to Location: distance_to(another_location) and endpoint(distance, heading).
|
4
|
+
* a_location.endpoints_for_circle_with_radius(radius) returns an array of end points making a circle around location.
|
5
|
+
|
1
6
|
= v.1.3.1
|
2
7
|
* Wrote better docs for all classes.
|
3
8
|
* You can add points to Paths with Path.new(point, second_point, :color => :red, :fillcolor => :blue)
|
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{google_static_maps_helper}
|
8
|
-
s.version = "1.3.
|
8
|
+
s.version = "1.3.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Thorbj\303\270rn Hermansen"]
|
12
|
-
s.date = %q{2009-10-
|
12
|
+
s.date = %q{2009-10-31}
|
13
13
|
s.description = %q{This gem provides a simple interface to the Google Static Maps V2 API.}
|
14
14
|
s.email = %q{thhermansen@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -33,8 +33,6 @@ module GoogleStaticMapsHelper
|
|
33
33
|
#
|
34
34
|
# Provides a simple DSL stripping away the need of manually instantiating classes
|
35
35
|
#
|
36
|
-
# *NOTE* Paths are not supported by the DSL yet.
|
37
|
-
#
|
38
36
|
# Usage:
|
39
37
|
#
|
40
38
|
# # First of all, you might want to set your key etc
|
@@ -46,6 +44,7 @@ module GoogleStaticMapsHelper
|
|
46
44
|
# url = GoogleStaticMapsHelper.url_for do
|
47
45
|
# marker :lng => 1, :lat => 2
|
48
46
|
# marker :lng => 3, :lat => 4
|
47
|
+
# path {:lng => 5, :lat => 6}, {:lng => 7, :lat => 7}
|
49
48
|
# end
|
50
49
|
#
|
51
50
|
# # You can send in key, size etc to url_for
|
@@ -6,6 +6,9 @@ module GoogleStaticMapsHelper
|
|
6
6
|
# and Paths' points.
|
7
7
|
#
|
8
8
|
class Location
|
9
|
+
EARTH_RADIUS_KM = 6371 # :nodoc:
|
10
|
+
LAT_LNG_PRECISION = 6 # :nodoc:
|
11
|
+
|
9
12
|
class NoLngMethod < NoMethodError; end # Raised if incomming object doesnt respond to lng
|
10
13
|
class NoLatMethod < NoMethodError; end # Raised if incomming object doesnt respond to lat
|
11
14
|
class NoLatKey < ArgumentError; end # Raised if incomming Hash doesnt have key lat
|
@@ -31,6 +34,61 @@ module GoogleStaticMapsHelper
|
|
31
34
|
end
|
32
35
|
end
|
33
36
|
|
37
|
+
#
|
38
|
+
# Calculates the distance in meters to given location
|
39
|
+
#
|
40
|
+
# <tt>location</tt>:: Another location which you want the distance to
|
41
|
+
#
|
42
|
+
def distance_to(location)
|
43
|
+
dLat = deg2rad(location.lat - lat)
|
44
|
+
dLon = deg2rad((location.lng - lng).abs)
|
45
|
+
|
46
|
+
dPhi = Math.log(Math.tan(deg2rad(location.lat) / 2 + Math::PI / 4) / Math.tan(deg2rad(lat) / 2 + Math::PI / 4));
|
47
|
+
q = (dLat.abs > 1e-10) ? dLat/dPhi : Math.cos(deg2rad(lat));
|
48
|
+
|
49
|
+
dLon = 2 * Math::PI - dLon if (dLon > Math::PI)
|
50
|
+
d = Math.sqrt(dLat * dLat + q * q * dLon * dLon);
|
51
|
+
|
52
|
+
(d * EARTH_RADIUS_KM * 1000).round
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# Returns a new <tt>Location</tt> which has given distance and heading from current location
|
57
|
+
#
|
58
|
+
# <tt>distance</tt>:: The distance in meters for the new Location from current
|
59
|
+
# <tt>heading</tt>:: The heading in degrees we should go from current
|
60
|
+
#
|
61
|
+
def endpoint(distance, heading)
|
62
|
+
d = (distance / 1000.0) / EARTH_RADIUS_KM;
|
63
|
+
heading = deg2rad(heading);
|
64
|
+
|
65
|
+
oX = lng * Math::PI / 180;
|
66
|
+
oY = lat * Math::PI / 180;
|
67
|
+
|
68
|
+
y = Math.asin(Math.sin(oY) * Math.cos(d) + Math.cos(oY) * Math.sin(d) * Math.cos(heading));
|
69
|
+
x = oX + Math.atan2(Math.sin(heading) * Math.sin(d) * Math.cos(oY), Math.cos(d) - Math.sin(oY) * Math.sin(y));
|
70
|
+
|
71
|
+
y = y * 180 / Math::PI;
|
72
|
+
x = x * 180 / Math::PI;
|
73
|
+
|
74
|
+
self.class.new(:lat => y, :lng => x)
|
75
|
+
end
|
76
|
+
|
77
|
+
#
|
78
|
+
# Returns ends poionts which will make up a circle around current location and have given radius
|
79
|
+
#
|
80
|
+
def endpoints_for_circle_with_radius(radius, steps = 30)
|
81
|
+
raise ArgumentError, "Number of points has to be in range of 1..360!" unless (1..360).include? steps
|
82
|
+
|
83
|
+
points = []
|
84
|
+
steps.times do |i|
|
85
|
+
points << endpoint(radius, i * 360 / steps)
|
86
|
+
end
|
87
|
+
points << points.first
|
88
|
+
|
89
|
+
points
|
90
|
+
end
|
91
|
+
|
34
92
|
#
|
35
93
|
# Returning the location as a string "lat,lng"
|
36
94
|
#
|
@@ -38,19 +96,36 @@ module GoogleStaticMapsHelper
|
|
38
96
|
[lat, lng].join(',')
|
39
97
|
end
|
40
98
|
|
99
|
+
|
100
|
+
[:lng, :lat].each do |attr|
|
101
|
+
define_method("#{attr}=") do |value|
|
102
|
+
instance_variable_set("@#{attr}", lng_lat_to_precision(value, LAT_LNG_PRECISION))
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
41
106
|
private
|
42
107
|
def extract_location_from_hash!(location_hash)
|
43
108
|
raise NoLngKey unless location_hash.has_key? :lng
|
44
109
|
raise NoLatKey unless location_hash.has_key? :lat
|
45
|
-
|
46
|
-
|
110
|
+
self.lat = location_hash.delete(:lat)
|
111
|
+
self.lng = location_hash.delete(:lng)
|
47
112
|
end
|
48
113
|
|
49
114
|
def extract_location_from_object(location)
|
50
115
|
raise NoLngMethod unless location.respond_to? :lng
|
51
116
|
raise NoLatMethod unless location.respond_to? :lat
|
52
|
-
|
53
|
-
|
117
|
+
self.lat = location.lat
|
118
|
+
self.lng = location.lng
|
119
|
+
end
|
120
|
+
|
121
|
+
def lng_lat_to_precision(number, precision)
|
122
|
+
rounded = (Float(number) * 10**precision).round.to_f / 10**precision
|
123
|
+
rounded = rounded.to_i if rounded.to_i == rounded
|
124
|
+
rounded
|
125
|
+
end
|
126
|
+
|
127
|
+
def deg2rad(deg)
|
128
|
+
deg * Math::PI / 180;
|
54
129
|
end
|
55
130
|
end
|
56
131
|
end
|
data/spec/location_spec.rb
CHANGED
@@ -49,4 +49,93 @@ describe GoogleStaticMapsHelper::Location do
|
|
49
49
|
it "should return to_url with its lat and lng value" do
|
50
50
|
GoogleStaticMapsHelper::Location.new(@location_hash).to_url.should == '10,20'
|
51
51
|
end
|
52
|
+
|
53
|
+
describe "reduce and round off lng and lat" do
|
54
|
+
before do
|
55
|
+
@location = GoogleStaticMapsHelper::Location.new(:lng => 0, :lat => 1)
|
56
|
+
end
|
57
|
+
|
58
|
+
[:lng, :lat].each do |attribute|
|
59
|
+
it "should not round #{attribute} when it is a number with a precision less than 6" do
|
60
|
+
@location.send("#{attribute}=", 12.000014)
|
61
|
+
@location.send(attribute).should == 12.000014
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should round #{attribute} when it is a number with a precision above 6" do
|
65
|
+
@location.send("#{attribute}=", 12.0000051)
|
66
|
+
@location.send(attribute).should == 12.000005
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should round and reduce #{attribute} when it's value is a float which can be represented with a descrete value" do
|
70
|
+
@location.send("#{attribute}=", 12.00000000001)
|
71
|
+
@location.send(attribute).to_s.should == "12"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "helper methods" do
|
77
|
+
[
|
78
|
+
[{:lat => 60, :lng => 0}, 0],
|
79
|
+
[{:lat => 60, :lng => 1}, 55597],
|
80
|
+
[{:lat => 61, :lng => 0}, 111195],
|
81
|
+
[{:lat => 60, :lng => 0.01}, 556]
|
82
|
+
].each do |point_distance|
|
83
|
+
it "should calculate correct distance to another location" do
|
84
|
+
another_point, expected_distance = point_distance
|
85
|
+
base = GoogleStaticMapsHelper::Location.new(:lat => 60, :lng => 0)
|
86
|
+
another_point = GoogleStaticMapsHelper::Location.new(another_point)
|
87
|
+
base.distance_to(another_point).should == expected_distance
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
[
|
93
|
+
[1000, 360, {:lat => 59.841958, :lng => 10.439303}],
|
94
|
+
[1000, 324, {:lat => 59.84024, :lng => 10.428782}],
|
95
|
+
[1000, 114, {:lat => 59.829306, :lng=> 10.45565}],
|
96
|
+
[1000, 18, {:lat => 59.841518, :lng => 10.444835}]
|
97
|
+
].each do |distance_heading_new_point|
|
98
|
+
it "should calculate new end point with given distance and heading" do
|
99
|
+
distance, heading, excpexted_point = distance_heading_new_point
|
100
|
+
base = GoogleStaticMapsHelper::Location.new(:lat => 59.832964751405214, :lng => 10.439303436108082)
|
101
|
+
new_point = base.endpoint(distance, heading)
|
102
|
+
|
103
|
+
new_point.lat.should == excpexted_point[:lat]
|
104
|
+
new_point.lng.should == excpexted_point[:lng]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "endpoints_for_circle_with_radius" do
|
109
|
+
it "should 30 + 1 end points for circle as default" do
|
110
|
+
base = GoogleStaticMapsHelper::Location.new(:lat => 59.832964751405214, :lng => 10.439303436108082)
|
111
|
+
endpoints = base.endpoints_for_circle_with_radius(1000)
|
112
|
+
endpoints.length.should == 31
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should have the same point as start and end (to make a closed circle)" do
|
116
|
+
base = GoogleStaticMapsHelper::Location.new(:lat => 59.832964751405214, :lng => 10.439303436108082)
|
117
|
+
endpoints = base.endpoints_for_circle_with_radius(1000)
|
118
|
+
endpoints.first.should == endpoints.last
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should reley on endpoint method to calculate new endpoints when creating circle points" do
|
122
|
+
base = GoogleStaticMapsHelper::Location.new(:lat => 59.832964751405214, :lng => 10.439303436108082)
|
123
|
+
base.should_receive(:endpoint).with(1000, 0 * 360 / 4)
|
124
|
+
base.should_receive(:endpoint).with(1000, 1 * 360 / 4)
|
125
|
+
base.should_receive(:endpoint).with(1000, 2 * 360 / 4)
|
126
|
+
base.should_receive(:endpoint).with(1000, 3 * 360 / 4)
|
127
|
+
endpoints = base.endpoints_for_circle_with_radius(1000, 4)
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should raise argument error if number of points asked to returned when creating a circle is above 360" do
|
131
|
+
base = GoogleStaticMapsHelper::Location.new(:lat => 59, :lng => 10)
|
132
|
+
lambda {base.endpoints_for_circle_with_radius(1000, 361)}.should raise_error(ArgumentError)
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should raise argument error if number of points asked to returned when creating a circle is less than 1" do
|
136
|
+
base = GoogleStaticMapsHelper::Location.new(:lat => 59, :lng => 10)
|
137
|
+
lambda {base.endpoints_for_circle_with_radius(1000, 0)}.should raise_error(ArgumentError)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
52
141
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: google_static_maps_helper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.3.
|
4
|
+
version: 1.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- "Thorbj\xC3\xB8rn Hermansen"
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-11-01 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|