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.
@@ -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 of generating a Google Static Maps URL
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 url for map with these markers. Please note that if you do use this approach you have to set key, size and sensor setting.
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 into the Map object and is used
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
- The easiest way to build a marker is by simply sending in an object which responds to lng and lat, or
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 image, Google key, what zoom level and center point it should be fixed to
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
+ 1.3.2
@@ -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.0"
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-28}
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
- @lat = location_hash.delete(:lat)
46
- @lng = location_hash.delete(:lng)
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
- @lat = location.lat
53
- @lng = location.lng
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
@@ -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.1
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-10-31 00:00:00 +01:00
12
+ date: 2009-11-01 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency