ym4r 0.3.2 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -1,5 +1,24 @@
1
1
  =YM4R
2
- This is YM4R 0.3.2. The goal of YM4R (Yellow Maps For Ruby) is to ease the use of the Google Maps (including the Geocoding API) and Yahoo! Maps API's (including the Geocoding, Map Image, Traffic and Local Search API's) from Ruby and Rails.
2
+ This is YM4R 0.4.1. The goal of YM4R (Yellow Maps For Ruby) is to ease the use of the Google Maps (including the Geocoding API) and Yahoo! Maps API's (including the Geocoding, Map Image, Traffic and Local Search API's) from Ruby and Rails.
3
+
4
+ ==Installing
5
+ ===As a Gem
6
+ Just type under a command line:
7
+ gem install ym4r
8
+ And the last version will be automatically downloaded and installed.
9
+
10
+ ===In the lib directory of a Rails project
11
+ It can be useful if you cannot install global gems on the machine where you are going to deploy your application (for example on a shared host).
12
+
13
+ You first need to unpack the gem. To do this, if the gem is installed on your local machine, type:
14
+ gem unpack ym4r
15
+
16
+ You can also directly untar the gem (after having downloaded it first) if you prefer:
17
+ tar xf ym4r-0.3.2.gem
18
+ then:
19
+ tar xzf data.tar.gz
20
+
21
+ Then copy the content of the lib directory of the gem into the lib directory of your rails application: You should have in RAILS_ROOT/lib the ym4.rb file and the ym4r directory (plus a tasks folder; which was already there before). Contrary to what is done for a plugin, you will still need to require and include in your application the files and modules you are interested in.
3
22
 
4
23
  ==Operations
5
24
  ===Google Maps
@@ -14,6 +33,13 @@ You should load the Google Maps part of YM4R this way:
14
33
  require 'ym4r/google_maps'
15
34
  include Ym4r::GoogleMaps
16
35
 
36
+ ===Managing the API key(s)
37
+ Google Maps requires a domain dependent key in order to use the library. So if you use your app from both <tt>localhost</tt> (for developing and testing) and some other domain like <tt>mydomain.com</tt>, you will need 2 sets of keys. You can get keys from http://www.google.com/apis/maps/signup.html. Your key will be valid for all the subdirectories of the URL you requested the key for.
38
+
39
+ Starting with version 0.4.1, there are 2 ways to manage the keys:
40
+ - The first is to have only 1 key at a time and change the content of the <tt>YM4R_ROOT/lib/ym4r/google_maps/config/config.yml</tt> file as part of the deployment process. Managing the keys is then your sole responsibility. If you do that, you should have only one key valid for your currently deployment location as the content of the <tt>config.yml</tt> file. Basically that's it.
41
+ - The other one is to have a YAML hash as the content of the <tt>YM4R_ROOT/lib/ym4r/google_maps/config/config.yml</tt> file. Each key will be a host and each value the corresponding API key for the host. Then in the view, when outputting the headers with the <tt>GMap.header</tt> class method, you should pass the option <tt>:host</tt> with the host as the value. Of course this host should not be hardcoded but taken from the request. For example in Rails, you could pass: <tt>:host => @request.host</tt>.
42
+
17
43
  ====Naming conventions
18
44
  The names of the Ruby class follow the ones in the JavaScript Google Maps API v2, except for GMap2, which in Ruby code is called simply GMap. To know what is possible to do with each class, you should refer to the documentation available on Google website.
19
45
 
@@ -39,7 +65,7 @@ which is strictly equivalent to
39
65
  The map is represented by a GMap object. You need to pass to the constructor the id of a DIV that will contain the map. You have to place this DIV yourself in your HTML template. You can also optionnally pass to the constructor the JavaScript name of the variable that will reference the map, which by default will be global in JavaScript. You have convenience methods to setup the controls, the center, the zoom, overlays, map types and the icons (which are also global). You can also pass arbitrary JavaScript to +record_init+ and +record_global_init+. Since, by default, the initialization of the map is performed in a callback function, if you want to have a globally accessible variable, you need to use the +global+ version.
40
66
 
41
67
  Then in your template, you have 2 necessary calls:
42
- - <tt>GMap#header</tt>: Outputs the inclusion of the JavaScript file from Google to make use of the Google Maps API + a style declaration for VML objects, necessary to display polylines under IE.
68
+ - The static <tt>GMap.header</tt>: Outputs the inclusion of the JavaScript file from Google to make use of the Google Maps API and by default a style declaration for VML objects, necessary to display polylines under IE. This default can be overriddent by passing <tt>:with_vml => false</tt> as option to the +header+ method. You can also pass to this method a <tt>:host</tt> option in order to select the correct key for the location where your app is currently deployed. See the section "Managing the API key(s)" above for more details.
43
69
  - <tt>GMap#to_html</tt>: Outputs the initialization code of the map. By default, it outputs the +script+ tags and initializes the map in response to the onload event of the JavaScript window object.
44
70
 
45
71
  Starting with version 0.2.1, there is no need to manually wire in the body element the response to the +load+ and +unload+ events on the JavaScript window object.
@@ -96,13 +122,56 @@ Of course, with only 3 markers, the whole clusterer thing is totally useless. Us
96
122
  - <tt>:max_lines_per_info_box</tt>: Number of lines to display in the info window displayed when a cluster is clicked on by the user. Defaults to 10.
97
123
  - <tt>:icon</tt>: Icon to be used to mark a cluster. Defaults to G_DEFAULT_ICON (the classic red marker).
98
124
 
99
- Then to add the clusterer to the map, you proceed as with a classic overlay:
100
- map.addOverlay(clusterer)
125
+ Then to add the clusterer to the map at initialization time, you proceed as with a classic overlay:
126
+ @map.overlay_init clusterer
101
127
 
102
- To add new markers in RJS, you should do this:
128
+ To add new markers in RJS (if the clusterer has been declared globally with overlay_global_init), you should do this:
103
129
  page << clusterer.add_marker(marker,description)
104
130
  In this case, the <tt>:description</tt> passed to the GMarker contructor will not be taken into account. Only the +description+ passed in the call to +add_marker+ will.
105
131
 
132
+ ====GeoRss Overlay
133
+ An group of markers taken from a Rss feed containing location information in the basic geo (WGS83 lat/lon) vocabulary. See http://www.w3.org/2003/01/geo/ for more details. It is a bit confusing since this vocabulary is very often referred to as GeoRss, while being different from the current GeoRss standardization effort (http://georss.org). Maybe the latter vocabulary will be supported by YM4R in the future but it is not currently the case. The support for GeoRss relies on the MGeoRSS library by Mikel Maron (http://brainoff.com/gmaps/mgeorss.html), although a bit modified, mostly to have the GeoRssOverlay respect the GOverlay API.
134
+
135
+ <b>Note that the <tt>geoRssOverlay.js</tt> file found in the <tt>YM4R_ROOT/lib/ym4r/google_maps/javascript</tt> folder must be included in the HTML template in order to use this functionality.</b>
136
+
137
+ Here is how you would use it. First create the overlay at initialization:
138
+ def index
139
+ @map = GMap.new("map_div")
140
+ @map.control_init(:large_map => true)
141
+ @map.center_zoom_init([38.134557,-95.537109],0)
142
+ @map.overlay_init(GeoRssOverlay.new(url_for(:action => "earthquake_rss"))
143
+ end
144
+ Since it is not possible to make requests outside the domain where the current page comes from, there is a need for a proxy. With the GeoRssOverlay initialization above, the request will be made by the <tt>earthquake_rss</tt> action, where the address to find the RSS feed will be hardwired:
145
+ def earthquake_rss
146
+ result = Net::HTTP.get_response("earthquake.usgs.gov","/eqcenter/recenteqsww/catalogs/eqs7day-M5.xml")
147
+ render :xml => result.body
148
+ end
149
+ If you don't want to hardwire the RSS feed location in an action, you can. But you will have to pass the <tt>:proxy</tt> option to the GeoRssOVerlay constructor. When requesting the RSS feed, the browser will in fact call the proxy with the actual URL of the RSS feed in the +q+ parameter of the request. Here is how you would initialize the GeoRssOVerlay that way:
150
+ @map.overlay_init(GeoRssOverlay.new("http://earthquake.usgs.gov/eqcenter/recenteqsww/catalogs/eqs7day-M5.xml",
151
+ :proxy => url_for(:action => "proxy")))
152
+ And here is the <tt>proxy</tt> action:
153
+ def proxy
154
+ result = Net::HTTP.get_response(URI.parse(@params[:q]))
155
+ render :xml => result.body
156
+ end
157
+ You should probably do some checks to ensure the proxy is not abused but it is of your respnsibility.
158
+
159
+ Another option can be passed to the GeoRssOVerlay constructor to set an icon to be used by the markers of the overlay: <tt>:icon</tt>. By default it is equal to the default icon (the classic red one).
160
+
161
+ In the view, you should have something like the following:
162
+ <html>
163
+ <head><title>Testing GeoRss</title>
164
+ <%= GMap.header(:with_vml => false) %>
165
+ <%= javascript_include_tag("geoRssOverlay") %>
166
+ <%= @map.header_width_height(600,400) %>
167
+ <%= @map.to_html %>
168
+ </head>
169
+ <body>
170
+ <%= @map.div %>
171
+ </body>
172
+ </html>
173
+ Note the inclusion of the <tt>geoRssOverlay.js</tt> file.
174
+
106
175
  ====Adding new map types
107
176
  It is now possible to easily add new map types, on top of the already existing ones, like G_SATELLITE_MAP or G_NORMAL_MAP. The imagery for these new map types can come from layers of the standard map types or can be taken either from a WMS server or from pretiled images on a server (that can be generated with a tool that comes with the library).
108
177
 
@@ -244,19 +313,20 @@ Here is an example of request:
244
313
  Preliminary support for this API has been added. It works along the same lines as with the Google Maps API but it is not very polished currently.
245
314
 
246
315
  ==Changes since last version
247
- - Addition of Clusterer class
248
- - Addition of a PreTiledLayerFromAction class
316
+ - Addition of a GeoRssOverlay to display overlays
317
+ - Addition of a way to manage multiple keys in the same config file
318
+ - Deprecation of the GMap instance method <tt>GMap#header</tt>, replaced with a GMap class method <tt>GMap.header</tt>, taking an option hash as parameter
249
319
 
250
320
  ==TODO
251
- - Add support for easy manipulation of external Google Maps-related libraries: Advanced tooltip manipulation, GeoRSS, KML,...
252
- - Documentation! Documentation! Documentation!
321
+ - Add support for easy manipulation of external Google Maps-related libraries: Advanced tooltip manipulation, KML,...
322
+ - Complete support of the currently standardizing GeoRss vocabulary.
253
323
  - Tutorials
254
324
 
255
325
  ==Disclaimer
256
326
  This software is not endorsed in any way by Yahoo! or Google.
257
327
 
258
328
  ==Acknowledgement
259
- YM4R bundles JavaScript libraries from John Deck (WMS layers on Google Maps) and Jef Poskanzer (Clusterer on Google Maps).
329
+ YM4R bundles JavaScript libraries from John Deck (WMS layers on Google Maps), Jef Poskanzer (Clusterer on Google Maps) and Mikel Maron (GeoRss on Google Maps).
260
330
 
261
331
  ==License
262
332
  YM4R is released under the MIT license. The <tt>clusterer.js</tt> file is redistributed with a different license (but still compatible with the MIT license). Check the top of the file in <tt>lib/ym4r/google_maps/javascript</tt> to know more.
@@ -0,0 +1,107 @@
1
+ // GeoRssOverlay: GMaps API extension to display a group of markers from
2
+ // a RSS feed
3
+ //
4
+ // Copyright 2006 Mikel Maron (email: mikel_maron yahoo com)
5
+ //
6
+ // The original version of this code is called MGeoRSS and can be found
7
+ // at the following adress:
8
+ // http://brainoff.com/gmaps/mgeorss.html
9
+ //
10
+ // Modified and bundled with YM4R in accordance with the following
11
+ // license:
12
+ //
13
+ // This work is public domain
14
+
15
+ function GeoRssOverlay(rssurl,icon,proxyurl){
16
+ this.rssurl = rssurl;
17
+ this.icon = icon;
18
+ this.proxyurl = proxyurl;
19
+ this.request = false;
20
+ this.markers = [];
21
+ }
22
+
23
+ GeoRssOverlay.prototype = new GOverlay();
24
+
25
+ GeoRssOverlay.prototype.initialize=function(map) {
26
+ this.map = map;
27
+ this.load();
28
+ }
29
+
30
+ GeoRssOverlay.prototype.redraw = function(force){
31
+ //nothing to do : the markers are already taken care of
32
+ }
33
+
34
+ GeoRssOverlay.prototype.remove = function(){
35
+ for(var i= 0, len = this.markers.length ; i< len; i++){
36
+ this.map.removeOverlay(this.markers[i]);
37
+ }
38
+ }
39
+
40
+ GeoRssOverlay.prototype.copy = function(){
41
+ var oCopy = new GeoRssOVerlay(this.rssurl,this.icon,this.proxyurl);
42
+ oCopy.markers = [];
43
+ for(var i = 0 , len = this.markers.length ;i < len ; i++){
44
+ oCopy.markers.push(this.markers[i].copy());
45
+ }
46
+ return oCopy;
47
+ }
48
+
49
+ GeoRssOverlay.prototype.load=function() {
50
+ if (this.request != false) {
51
+ return;
52
+ }
53
+ this.request = GXmlHttp.create();
54
+ if (this.proxyurl != undefined) {
55
+ this.request.open("GET",this.proxyurl + '?q=' + encodeURIComponent(this.rssurl),true);
56
+ } else {
57
+ this.request.open("GET",this.rssurl, true);
58
+ }
59
+ var m = this;
60
+ this.request.onreadystatechange = function() {
61
+ m.callback();
62
+ }
63
+ this.request.send(null);
64
+ }
65
+
66
+ GeoRssOverlay.prototype.callback = function() {
67
+ if (this.request.readyState == 4) {
68
+ if (this.request.status == "200") {
69
+ var xmlDoc = this.request.responseXML;
70
+ var items = xmlDoc.documentElement.getElementsByTagName("item");
71
+ for (var i = 0; i < items.length; i++) {
72
+ try {
73
+ var marker = this.createMarker(items[i]);
74
+ this.markers.push(marker);
75
+ this.map.addOverlay(marker);
76
+ } catch (e) {
77
+ }
78
+ }
79
+ }
80
+ this.request = false;
81
+ }
82
+ }
83
+
84
+ GeoRssOverlay.prototype.createMarker = function(item) {
85
+ var title = item.getElementsByTagName("title")[0].childNodes[0].nodeValue;
86
+ var description = item.getElementsByTagName("description")[0].childNodes[0].nodeValue;
87
+ var link = item.getElementsByTagName("link")[0].childNodes[0].nodeValue;
88
+
89
+ /* namespaces are handled by spec in moz, not in ie */
90
+ if (navigator.userAgent.toLowerCase().indexOf("msie") < 0) {
91
+ var lat = item.getElementsByTagNameNS("http://www.w3.org/2003/01/geo/wgs84_pos#","lat")[0].childNodes[0].nodeValue;
92
+ var lng = item.getElementsByTagNameNS("http://www.w3.org/2003/01/geo/wgs84_pos#","long")[0].childNodes[0].nodeValue;
93
+ } else {
94
+ var lat = item.getElementsByTagName("geo:lat")[0].childNodes[0].nodeValue;
95
+ var lng = item.getElementsByTagName("geo:long")[0].childNodes[0].nodeValue;
96
+ }
97
+
98
+ var point = new GLatLng(parseFloat(lat), parseFloat(lng));
99
+ var marker = new GMarker(point,{'title': title});
100
+ var html = "<a href=\"" + link + "\">" + title + "</a><p/>" + description;
101
+
102
+ GEvent.addListener(marker, "click", function() {
103
+ marker.openInfoWindowHtml(html);
104
+ });
105
+
106
+ return marker;
107
+ }
@@ -20,10 +20,21 @@ module Ym4r
20
20
  @global_init = []
21
21
  end
22
22
 
23
- #Outputs the header necessary to use the Google Maps API. By default, it also outputs a style declaration for VML elements.
23
+ #Deprecated. Use the static version instead.
24
24
  def header(with_vml = true)
25
- a = "<script src=\"http://maps.google.com/maps?file=api&v=2&key=#{API_KEY}\" type=\"text/javascript\"></script>\n"
26
- a << "<style type=\"text/css\">\n v\:* { behavior:url(#default#VML);}\n</style>" if with_vml
25
+ GMap.header(:with_vml => with_vml)
26
+ end
27
+
28
+ #Outputs the header necessary to use the Google Maps API. By default, it also outputs a style declaration for VML elements (can be changed with the option <tt>:with_vml</tt>). You can also pass a host with the <tt>:host</tt> option: If you use Rails, you should pass <tt>:host => @request.host</tt>. This host must have a corresponding API key in the config.yml file. If you don't care about multiple hosts or want to manage the keys yourself, you should not pass the <tt>:host</tt> option and the config.yml should only contain a single API key.
29
+ def self.header(options = {})
30
+ options[:with_vml] = true unless options.has_key?(:with_vml)
31
+ if options.has_key?(:host)
32
+ api_key = API_KEY[options[:host]]
33
+ else
34
+ api_key = API_KEY
35
+ end
36
+ a = "<script src=\"http://maps.google.com/maps?file=api&v=2&key=#{api_key}\" type=\"text/javascript\"></script>\n"
37
+ a << "<style type=\"text/css\">\n v\:* { behavior:url(#default#VML);}\n</style>" if options[:with_vml]
27
38
  a
28
39
  end
29
40
 
@@ -106,6 +106,7 @@ module Ym4r
106
106
  #Used to bind a ruby variable to an already existing JavaScript one. It doesn't have to be a variable in the sense "var variable" but it can be any valid JavaScript expression that has a value.
107
107
  class Variable
108
108
  include MappingObject
109
+
109
110
  def initialize(variable)
110
111
  @variable = variable
111
112
  end
@@ -117,6 +118,8 @@ module Ym4r
117
118
  def to_s
118
119
  @variable + ";"
119
120
  end
121
+
122
+ UNDEFINED = Variable.new("undefined")
120
123
  end
121
124
  end
122
125
  end
@@ -92,7 +92,33 @@ module Ym4r
92
92
  a << ")"
93
93
  end
94
94
  end
95
-
95
+ #A basic Latitude/longitude point.
96
+ class GLatLng
97
+ include MappingObject
98
+ attr_accessor :lat,:lng,:unbounded
99
+
100
+ def initialize(latlng,unbounded = nil)
101
+ @lat = latlng[0]
102
+ @lng = latlng[1]
103
+ @unbounded = unbounded
104
+ end
105
+ def create
106
+ unless @unbounded
107
+ "new GLatLng(#{@lat},#{@lng})"
108
+ else
109
+ "new GLatLng(#{@lat},#{@lng},#{@unbounded})"
110
+ end
111
+ end
112
+ end
113
+
114
+ #A rectangular bounding box, defined by its south-western and north-eastern corners.
115
+ class GLatLngBounds < Struct.new(:sw,:ne)
116
+ include MappingObject
117
+ def create
118
+ "new GLatLngBounds(#{sw},#{ne})"
119
+ end
120
+ end
121
+
96
122
  #A GOverlay representing a group of GMarkers. The GMarkers can be identified with an id, which can be used to show the info window of a specific marker, in reponse, for example, to a click on a link. The whole group can be shown on and off at once. It should be declared global at initialization time to be useful.
97
123
  class GMarkerGroup
98
124
  include MappingObject
@@ -117,11 +143,15 @@ module Ym4r
117
143
  #Makes the link with the Clusterer2 library by Jef Poskanzer (slightly modified though). Is a GOverlay making clusters out of its GMarkers, so that GMarkers very close to each other appear as one when the zoom is low. When the zoom gets higher, the individual markers are drawn.
118
144
  class Clusterer
119
145
  include MappingObject
120
- attr_accessor :markers,:options
146
+ attr_accessor :markers,:icon, :max_visible_markers, :grid_size, :min_markers_per_cluster , :max_lines_per_info_box
121
147
 
122
148
  def initialize(markers = [], options = {})
123
149
  @markers = markers
124
- @options = options
150
+ @icon = options[:icon] || GIcon::DEFAULT
151
+ @max_visible_markers = options[:max_visible_markers] || 150
152
+ @grid_size = options[:grid_size] || 5
153
+ @min_markers_per_cluster = options[:min_markers_per_cluster] || 5
154
+ @max_lines_per_info_box = options[:max_lines_per_info_box] || 10
125
155
  end
126
156
 
127
157
  def create
@@ -129,40 +159,27 @@ module Ym4r
129
159
  add_description(marker)
130
160
  end.join(",") + ']'
131
161
 
132
- "new Clusterer(#{js_marker},#{MappingObject.javascriptify_variable(@options[:icon] || GIcon::DEFAULT)},#{@options[:max_visible_markers] || 150},#{@options[:grid_size] || 5},#{@options[:min_markers_per_cluster] || 5},#{@options[:max_lines_per_info_box] || 10})"
162
+ "new Clusterer(#{js_marker},#{MappingObject.javascriptify_variable(@icon)},#{MappingObject.javascriptify_variable(@max_visible_markers)},#{MappingObject.javascriptify_variable(@grid_size)},#{MappingObject.javascriptify_variable(@min_markers_per_cluster)},#{MappingObject.javascriptify_variable(@max_lines_per_info_box)})"
133
163
  end
134
164
 
135
165
  private
136
166
  def add_description(marker)
137
167
  "addDescriptionToMarker(#{MappingObject.javascriptify_variable(marker)},#{MappingObject.javascriptify_variable(marker.options[:description] || marker.options[:title] || '')})"
138
168
  end
139
-
140
169
  end
141
170
 
142
- #A basic Latitude/longitude point.
143
- class GLatLng
171
+ class GeoRssOverlay
144
172
  include MappingObject
145
- attr_accessor :lat,:lng,:unbounded
173
+ attr_accessor :url, :proxy, :icon
146
174
 
147
- def initialize(latlng,unbounded = nil)
148
- @lat = latlng[0]
149
- @lng = latlng[1]
150
- @unbounded = unbounded
151
- end
152
- def create
153
- unless @unbounded
154
- "new GLatLng(#{@lat},#{@lng})"
155
- else
156
- "new GLatLng(#{@lat},#{@lng},#{@unbounded})"
157
- end
175
+ def initialize(url, options = {})
176
+ @url = url
177
+ @icon = options[:icon] || GIcon::DEFAULT
178
+ @proxy = options[:proxy] || Variable::UNDEFINED
158
179
  end
159
- end
160
-
161
- #A rectangular bounding box, defined by its south-western and north-eastern corners.
162
- class GLatLngBounds < Struct.new(:sw,:ne)
163
- include MappingObject
164
- def create
165
- "new GLatLngBounds(#{sw},#{ne})"
180
+
181
+ def create
182
+ "new GeoRssOverlay(#{MappingObject.javascriptify_variable(@url)},#{MappingObject.javascriptify_variable(@icon)},#{MappingObject.javascriptify_variable(@proxy)})"
166
183
  end
167
184
  end
168
185
 
@@ -24,7 +24,7 @@ spec = Gem::Specification::new do |s|
24
24
  s.platform = Gem::Platform::RUBY
25
25
 
26
26
  s.name = 'ym4r'
27
- s.version = "0.3.2"
27
+ s.version = "0.4.1"
28
28
  s.summary = "Using Google Maps and Yahoo! Maps from Ruby and Rails"
29
29
  s.description = <<EOF
30
30
  EOF
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: ym4r
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.3.2
7
- date: 2006-06-23 00:00:00 +05:00
6
+ version: 0.4.1
7
+ date: 2006-07-01 00:00:00 +05:00
8
8
  summary: Using Google Maps and Yahoo! Maps from Ruby and Rails
9
9
  require_paths:
10
10
  - lib
@@ -60,6 +60,7 @@ files:
60
60
  - lib/ym4r/yahoo_maps/flash/widget.rb
61
61
  - lib/ym4r/google_maps/config/config.yml
62
62
  - lib/ym4r/google_maps/javascript/clusterer.js
63
+ - lib/ym4r/google_maps/javascript/geoRssOverlay.js
63
64
  - lib/ym4r/google_maps/javascript/markerGroup.js
64
65
  - lib/ym4r/google_maps/javascript/wms-gs.js
65
66
  - tools/tile_image.rb