ym4r 0.4.1 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -1,5 +1,12 @@
1
1
  =YM4R
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.
2
+ This is YM4R 0.5.1. YM4R has features to ease the use of the Google Maps Geocoding API and the Yahoo! Maps building block API's from Ruby. It also offers 2 tools to create tiles to be used with user-defined layers in Google Maps. If you are looking to use the main Google Maps API from your Rails application, you should use the YM4R/GM plugin for Rails (http://thepochisuperstarmegashow.com/projects/#ym4r).
3
+
4
+ ==Relation between the YM4R gem and the YM4R/GM plugin for Rails
5
+ They are completely independent from each other.
6
+
7
+ With the plugin, you don't need the YM4R gem anymore, unless you want to use the Yahoo! Maps Building Block API's, the tilers or the Google Maps geocoding REST API. All of these functionalities are detailed in the current document.
8
+
9
+ Conversely, the YM4R gem does not need the plugin to work.
3
10
 
4
11
  ==Installing
5
12
  ===As a Gem
@@ -18,238 +25,10 @@ You can also directly untar the gem (after having downloaded it first) if you pr
18
25
  then:
19
26
  tar xzf data.tar.gz
20
27
 
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.
28
+ Then copy the content of the lib directory of the gem into the lib directory of your rails application: You should have in <tt>RAILS_ROOT/lib</tt> the <tt>ym4.rb</tt> file and the <tt>ym4r</tt> 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.
22
29
 
23
30
  ==Operations
24
- ===Google Maps
25
- You can use the library to display Google maps easily with any ruby-based web framework. The version of the API used is v2. The library is engineered so updates to the map through Ajax are possible. I have made available 2 in-depth tutorials to show some of the functionalities of the library and how it can be integrated with GeoRuby and the Spatial Adapter for Rails:
26
- - http://thepochisuperstarmegashow.com/2006/06/02/ym4r-georuby-spatial-adapter-demo/
27
- - http://thepochisuperstarmegashow.com/2006/06/03/google-maps-yahoo-traffic-mash-up/
28
- For the Geocoding API, see the section after this one.
29
- Following is some notes about manipulating Google Maps with the library:
30
-
31
- ====Loading the library
32
- You should load the Google Maps part of YM4R this way:
33
- require 'ym4r/google_maps'
34
- include Ym4r::GoogleMaps
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
-
43
- ====Naming conventions
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.
45
-
46
- On top of that, you have some convenience methods for initializing the map (in the GMap class). Also, the constructors of some classes accept different types of arguments to be converted later in the correct JavaScript format. For example, the +GMarker+ aclass accepts an array of 2 floats as parameter, in addition of a GLatLng object, to indicate its position. It also facilitates the attribution of an HTML info window, displayed when the user clicks on it, since you can pass to the constructor an options hash with the <tt>:info_window</tt> key and the text to display as the value, instead of having to wire the response to a click event yourself.
47
-
48
- ====Binding JavaScript and Ruby
49
- Since the Google Maps API uses JavaScript to create and manipulate a map, most of what the library does is outputting JavaScript, although some convenience methods are provided to simplify some common operations at initialization time. When you create a YM4R mapping object (a Ruby object which includes the MappingObject module) and call methods on it, these calls are converted by the library into JavaScript code. At initialization time, you can pass arbitrary JavaScript code to the <tt>GMap#record_init</tt> and <tt>GMap#record_global_init</tt>.Then, at update time, if you use Ruby-on-Rails as your web framework, you can update your map through RJS by passing the result of the method calls to the <tt>page << </tt> method to have it then interpreted by the browser.
50
-
51
- For example, here is a typical initialization sequence for a map
52
- @map = GMap.new("map_div")
53
- @map.control_init(:large_map => true,:map_type => true)
54
- @map.center_zoom_init([35.12313,-110.567],12)
55
- @map.record_init @map.add_overlay(GMarker.new([35.12878, -110.578],:title => "Hello!"))
56
-
57
- While +center_zoom_init+ and +control_init+ are one of the rare convenience methods that do not output JavaScript, the +add_overlay+ does. Actually, if you look at the code of the GMap class, you won't find any +add_overlay+ method, although in the documentation of the GMap2 class from the Google Maps API documentation, you will find something about the +addOverlay+ JavaScript method. In fact, when you call on a mapping object an unknow method, it is converted to a javascriptified version of it, along with its arguments, and a string of JavaScript code is output. So the <tt>@map.add_overlay...</tt> above is converted to <tt>"map.addOverlay(new GMarker(GLatLng.new(35.12878, -110.578),{title:\"Hello!\"}))"</tt>, which is then passed to the +record_init+ method of a Ruby GMap object to be later output along with the rest of the initialization code.
58
-
59
- Starting with version 0.1.4 of the YM4R library, this call to +record_init+ to add an overlay could have been replaced with a call to +overlay_init+:
60
- @map.overlay_init GMarker.new([35.12878, -110.578],:title => "Hello!")
61
- which is strictly equivalent to
62
- @map.record_init @map.add_overlay(new GMarker([35.12878, -110.578],:title => "Hello!"))
63
-
64
- ====Initialization of the map
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.
66
-
67
- Then in your template, you have 2 necessary calls:
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.
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.
70
-
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.
72
-
73
- ====Update of the map
74
- You are able to update the map through Ajax. For example, in Ruby-on-Rails, you have something called RJS, which sends JavaScript created on the server as a response to an action, which is later interpreted by the browser. It is usually used for visual effects and replacing or adding elements. It can also accept arbitrary JavaScript and this is what YM4R uses.
75
-
76
- For example, if you want to add a marker to the map, you need to do a few things. First, you have to bind a Ruby mapping object to the global JavaScript map variable. By default its name is +map+, but you could have overriden that at initialization time. You need to do something like this:
77
- @map = Variable.new("map")
78
- +map+ in the Variable constructor is the name of the global JavaScript map variable. Then any method you call on <tt>@map</tt> will be converted in JavaScript to a method called on +map+. In your RJS code, you would do something like this to add a marker:
79
- page << @map.add_overlay(GMarker.new([123123.1,12313.76],:title => "Hello again!"))
80
- What is sent to the browser will be the fllowing JavaScript code:
81
- map.addOverlay(new GMarker(new GLatLng(123123.1,12313.76),{title:\"Hello again!\"}))
82
-
83
- ====GMarkerGroup
84
- A new type of GOverlay is available, called GMarkerGroup. To use it you would have to include in your HTML template the JavaScript file markerGroup.js found in the <tt>lib/ym4r/google_maps/javascript</tt> directory of the distribution.
85
-
86
- It is useful in 2 situations:
87
- - Display and undisplay a group of markers without referencing all of them. You just declare your marker group globally and call +activate+ and +deactivate+ on this group in response, for example, to clicks on links on your page.
88
- - Keeping an index of markers, for example, in order to show one of these markers in reponse to a click on a link (the way Google Local does with the results of the search).
89
-
90
- Here is how you would use it from your Ruby code:
91
- @map = GMap.new("map_div")
92
- marker1 = GMarker.new([123.55,123.988],:info_window => "Hello from 1!")
93
- marker2 = GMarker.new([87.123,18.9],:info_window =>"Hello from 2!")
94
- @map.overlay_global_init(GMarkerGroup.new(true,
95
- 1 => marker1,
96
- 2 => marker2),"myGroup")
97
- Here I have created an active (ie which is going to be displayed when the map is created) marker group called +myGroup+ with 2 markers, which I want to reference later. If I did not want to reference them later (I just want to be able to display and undisplay the marker group), I could have passed an array instead of the hash.
98
-
99
- Then in your template, you could have that:
100
- <a href="#" onclick="myGroup.showMarker(1);return false;">Click here to display marker1</a>
101
- <a href="#" onclick="myGroup.showMarker(2);return false;">Click here to display marker2</a>
102
- <%= @map.to_div %>
103
- When you click on one of the links, the corresponding marker has its info window displayed.
104
-
105
- You can call +activate+ and +deactivate+ to display or undisplay a group of markers. You can add new markers with <tt>addMarker(marker,id)</tt>. Again if you don't care about referencing the marker, you don't need to pass an id. If the marker group is active, the newly added marker will be displayed immediately. Otherwise it will be displayed the next time the group is activated. Finally, since it is an overlay, the group will be removed when calling clearOverlays on the GMap object.
106
-
107
- ====Clusterer
108
- A Clusterer is a type of overlay that contains a potentially very large group of markers. It is engineered so markers close to each other and undistinguishable from each other at some level of zoom, appear as only one marker on the map at that level of zoom. Therefore it is possible to have a very large number of markers on the map at the same time and not having the map crawl to a halt in order to display them. It has been slightly modified from Jef Poskanzer's original Clusterer2 code (see http://www.acme.com/javascript/ for the original version). The major difference with that version is that, in YM4R, the clusterer is a GOverlay and can therefore be added to the map with <tt>map.addOverlay(clusterer)</tt> and is cleared along with the rest of overlays when <tt>map.clearOverlays()</tt> is called.
109
-
110
- <b>In order to use a clusterer, you should include in your template page the clusterer.js file that can be found in the <tt>lib/ym4r/gogle_maps/javascript/</tt> directory of the YM4R distribution.</b>
111
-
112
- To create a clusterer, you should first create an array of all the GMarkers that you want to include in the clusterer (you can still add more after the clusterer has been added to the map though). When GMarkers close together are grouped in one cluster (represented by another marker on the map) and the user clicks on the cluster marker, a list of markers in the cluster is displayed in the info windo with a description: By default it is equal to the +title+ of the markers (which is also displayed as a tooltip when hovering on the marker with the mouse). If you don't want that, you can also pass to the GMarker constructor a key-value pair with key <tt>:description</tt> to have a description different from the title. For example, here is how you would create a clusterer:
113
- markers = [GMarker.new([37.8,-90.4819],:info_window => "Hello",:title => "HOYOYO"),
114
- GMarker.new([37.844,-90.47819],:info_window => "Namaste",:description => "Chopoto" , :title => "Ay"),
115
- GMarker.new([37.83,-90.456619],:info_window => "Bonjour",:title => "Third"),
116
- ]
117
- clusterer = Clusterer.new(markers,:max_visible_markers => 2)
118
- Of course, with only 3 markers, the whole clusterer thing is totally useless. Usually, using the clusterer starts being interesting with hundreds of markers. The options to pass the clusterer are:
119
- - <tt>:max_visible_markers</tt>: Below this number of markers, no clustering is performed. Defaults to 150.
120
- - <tt>:grid_size</tt>: The clusterer divides the visible area into a grid of this size. Defaults to 5.
121
- - <tt>:min_markers_per_cluster</tt>: Below this number of markers a cluster is not formed. Defaults to 5.
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.
123
- - <tt>:icon</tt>: Icon to be used to mark a cluster. Defaults to G_DEFAULT_ICON (the classic red marker).
124
-
125
- Then to add the clusterer to the map at initialization time, you proceed as with a classic overlay:
126
- @map.overlay_init clusterer
127
-
128
- To add new markers in RJS (if the clusterer has been declared globally with overlay_global_init), you should do this:
129
- page << clusterer.add_marker(marker,description)
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.
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
-
175
- ====Adding new map types
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).
177
-
178
- For exemple, here is how you would setup layers from a public WMS server of the DMAP team of the American Navy:
179
- layer = WMSLayer.new("http://columbo.nrlssc.navy.mil/ogcwms/servlet/WMSServlet/AccuWeather_Maps.wms",
180
- "20:3,6:3,0:27,0:29,6:19","",
181
- {'prefix' => "Map Copyright 2006", 'copyright_texts'=> ["DMAP"]},
182
- true,:mapserver,0..17,0.8,"png")
183
- This sets up a connection to a WMS service, requesting layers <tt>20:3,6:3,0:27,0:29,6:19</tt> (you would have to look at the GetCapabilities document of the service to know what the valid layers are), with default styles and a copyright notice attributing the data to DMAP. The layers are valid for zoom levels 0 to 17 (all the zoom levels). The images will be 80% opaque and will come as PNG.
184
-
185
- The arguments +true+ and <tt>:mapserver</tt> warrant some explanation. The Google Maps are in the Simple Mercator projection in the WGS84 datum and currently do not support the display of data in projections other than that, although it may change in the future. Unfortunately, different WMS servers do not identify this projection the same way. So you can give to the WMSLayer constructor your server type and it will figure out what is the correct identifier. Currently, this works only for <tt>:mapserver</tt> (EPSG:54004) and <tt>:geoserver</tt> (EPSG:41001). For others you can directly pass a number corresponding to the EPSG definition of the simple Mercator projection of your server. On top of that, some servers just don't support the Simple Mercator projection. This is why there is a +true+ argument before <tt>:mapserver</tt> in the example. It is in order to tell the WMSLayer that it should request its tiles using LatLon coordinates in the WGS84 datum (which should be supported by any server). Unfortunately it is not perfect since the deformation is quite important for low zoom levels (< 5 for the US). Higher than that, the deformation is not that visible. However, if you control the WMS server, it is recommended that you have a +false+ in this argument and setup the Mercator projection in your server if it is not done by default.
186
-
187
- <b>Note that you need to include the wms-gs.js javascript file (in the <tt>lib/ym4r/google_maps/javascript</tt> folder) in your HTML page in order to make use of the WMSLayer functionality. It uses code by John Deck with the participation of others (read the top of the javascript file to know more).</b>
188
-
189
- Here is how to define a pretiled layer:
190
- layer = PreTiledLayer.new("http://localhost:3000/tiles",
191
- {'prefix' => "Map C 2006", 'copyright_texts' => ["Open Atlas"]},
192
- 13..13,0.7,"gif")
193
- I tell the PreTiledLayer constructor where I can find the tiles, setup the Copyright string, the valid zooms for the map, the opacity and the format. Tiles must have standardized names: <tt>tile_#{zoom}_#{x_tile}_#{y_tile}.#{format}</tt> (here the format is "gif"). You can use some tools (see below) to generate tiles in this format either from local maps or from WMS servers (useful to create tiles from geographic data files without having to run a map server or to cache images from slow servers).
194
-
195
- Instead of having the tiles requested directly, you can also decide to have an action on the server which takes care of it. You can used the class PreTiledLayerFromAction for this. In this case, the first argument of the constructor is an url of an action. The arguments +x+, +y+ and +z+ will be passed to it.
196
- layer = PreTiledLayerFromAction.new(url_for(:action => :get_tile),
197
- {'prefix' => "Map C 2006", 'copyright_texts' => ["Open Atlas"]},
198
- 13..14,0.7,"png")
199
- The other constructor arguments have the same meaning as PreTiledLayer. Here is an uninteresting example of action that serves tiles:
200
- def get_tile
201
- x = @params[:x]
202
- y = @params[:y]
203
- z = @params[:z]
204
- begin
205
- send_file "#{RAILS_ROOT}/public/tiles/tile_#{z}_#{x}_#{y}.png" ,
206
- :type => 'image/png', :disposition => 'inline'
207
- rescue Exception
208
- render :nothing => true
209
- end
210
- end
211
-
212
- You can add a layer to a new map type the following way:
213
- map_type = GMapType.new(layer,"My WMS")
214
- This is actually the simplest configuration possible. Your map type has only one data layer and is called "My WMS". You can add more that one layer: Either one that you have created yourself or existing ones. For example:
215
- map_type = GMapType.new([GMapType::G_SATELLITE_MAP.get_tile_layers[0],layer,GMapType::G_HYBRID_MAP.get_tile_layers[1]],
216
- "Test WMS")
217
- Here for the "Test WMS" map type, we also take the first layer of the "Satellite" map type in the background and overlay the second layer of the "Hybrid" map type (roads, country boundaries, etc... transparently overlaid on top of the preceding layers) so when the "Test WMS" map type is selected in the interface, all three layers will be displayed.
218
-
219
- Finally to add a map type to a GMap:
220
- @map.map_type_init(map_type)
221
- If you want to wipe out the existing map types (for example the 3 default ones), you can add a +false+ argument to the +map_type_init+ method and the +map_type+ will be the only one.
222
-
223
-
224
- ====How to generate tiles for use in pretiled layers
225
-
226
- The YM4R library provides 2 tools to generate tiles. They are in the +tools+ directory of the YM4R distribution. These tools can naturally be used to generate tiles even if you don't use the YM4R library to manipulate your Google Maps. You will just have to write the code to fetch the tiles by yourself.
227
-
228
- =====tile_wms.rb
229
- This is to generate tiles from an already existing WMS server. It can be useful if you don't want to setup a permanent WMS server but still want to display your geographic data files (any format compatible with your WMS server can then be used). It can also be used to cache data from public servers, which can sometimes be very slow to answer requests. Run "ruby tile_wms.rb" to know what the options are. These are very similar to the ones passed to the WMSTiler constructor, although the <tt>-g</tt> (<tt>--gmap-setting</tt>) needs to be explained in more details. It uses the data that comes from this tool: http://www.onnyturf.com/google/latlontotile.html. Basically you need to center the map at the zoom you need to get your desired extent. When you are satisfied, the tool will give you the following data: the X and Y values of the upper left corner tile, 17 minus the zoom level (this is because the V2 of the Google Maps API reversed the zoom order), the number of horizontal and vertical tiles that you want. You will then pass to the <tt>-g</tt> option 5 integers in the order previously described, for example:
230
- -g 300,383,7,3,2
231
- About the zoom level, if the tools tells you 7, you should pass 10 (17 - 7) to the <tt>-g</tt> option.
232
-
233
- Note that the zoom levels passed to the <tt>-z</tt> argument must be greater or equal to the zoom level passed to the <tt>-g</tt> argument.
234
-
235
- =====tile_image.rb
236
- This is to generate tiles from a local image. You will need the RMagick library for it to work. Basically, you should use this tool: http://open.atlas.free.fr/GMapsTransparenciesImgOver.php. You should have an image that will be displayed at the lowest zoom level that you want and use it with this tool. You can translate and scale the image until you are statisfied. When you are, you must click a link to gather information about the transformation needed for your image. You will need to pass this data to the <tt>-p</tt> option of the tool in the following order: X and Y of the top left corner of the map, 17 minus the zoom level, X and Y padding, scale. So for example, you will have the following argument:
237
- -p 2503,3667,4,241,115,0.8702928870292888
238
- Again, about the zoom level, if the tool tells you 7, you should pass 10 (17 - 7) to the <tt>-p</tt> option.
239
-
240
- About the padding, I had a slight problem on Firefox: I needed to substract 10 to the X and Y paddings given by the online tool to have the correct one to pass to the <tt>-p</tt> option. Maybe it has something to do with the 10-pixel bands at the top and left of the page.
241
-
242
- You should pass as many images to the tool as you have zoom levels in the <tt>-z</tt> argument: Each image will be used for one zoom level. The string order of the names of the image files must match the order of their corresponding zoom level. Moreover, each image should be exactly twice bigger (in both width and height) than the one in the zoom level immediately preceding it. Also note that the zoom levels passed to the <tt>-z</tt> argument must be greater or equal to the zoom level passed to the <tt>-p</tt> argument.
243
-
244
- By default, the tool will generate transparent borders. You can pass 4 integers (R, G, B and A) from 0 to 255 to the -b option to change that:
245
- -b 255,123,456,42
246
-
247
- Here is an example of command line execution using the image at http://open.atlas.free.fr/GMapsTransparenciesImgOver.php. If you place it over the satellite images, you would get the following parameters: 2503,3667,13,149,76,1.0 (I have accounted for both the reversing of the zoom for v2 and the 10-pixel drift of the padding in Firefox). I have also created another version of the image, at double the size in width and height. Normally you should do the opposite: Get the highest resolution possible (if you scan the map) and downsize it for all the desired zoom levels until you get the image to use in this online tool (lowest zoom level). If you use vector graphics, you can easily create versions at different resolutions. I placed the 2 images (called MAP_zoom1.jpg for the smallest and MAP_zoom2.jpg for the biggest) in directory <tt>./input</tt>. And I launch the tool like this:
248
- ruby tile_image.rb -o ./tiles -z 13..14 -p 2503,3667,13,149,76,1.0 ./input/*
249
- This will create tiles of the image for zoom levels 13 and 14. It should take just a few seconds.
250
-
251
31
  ===Google Maps Geocoding API
252
- Note that you have to require it separately from the Google Maps API described above.
253
32
  Here is an example of request:
254
33
  require 'ym4r/google_maps/geocoding'
255
34
  include Ym4r::GoogleMaps
@@ -287,7 +66,7 @@ Here is an example of request:
287
66
  :image_type => "png")
288
67
  +result+ is a MapImage::Result object contains the URL for the requested image. You can download this image to a file by calling +download_to+ and passing a path.
289
68
 
290
- ====Traffic
69
+ =====Traffic
291
70
  Here is an example of request:
292
71
  require 'ym4r/yahoo_maps/building_block'
293
72
  include Ym4r::YahooMaps::BuildingBlock
@@ -309,27 +88,45 @@ Here is an example of request:
309
88
  :query => "chinese")
310
89
  +results+ is a LocalSearch::ResultSet object (subclass of +Array+), containing LocalSearch::Result objects, each containing information about one hit on the Yahoo! local search.
311
90
 
312
- ===Yahoo! Maps JS-Flash API
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.
91
+ ===Tiling: Generating tiles to be used with pretiled layers in Google Maps
92
+
93
+ The YM4R library provides 2 tools to generate tiles. They are in the +tools+ directory of the YM4R gem distribution.
94
+
95
+ ====tile_wms.rb
96
+ This is to generate tiles from an already existing WMS server. It can be useful if you don't want to setup a permanent WMS server but still want to display your geographic data files (any format compatible with your WMS server can then be used). It can also be used to cache data from public servers, which can sometimes be very slow to answer requests. Run <tt>ruby tile_wms.rb -h</tt> to know what the options are. Most are self-explanatory, although the <tt>-g</tt> (<tt>--gmap-setting</tt>) needs to be explained in more details. It uses the data that comes from this tool: http://www.onnyturf.com/google/latlontotile.html. Basically you need to center the map at the zoom you need to get your desired extent. When you are satisfied, the tool will give you the following data: the X and Y values of the upper left corner tile, 17 minus the zoom level (this is because the V2 of the Google Maps API reversed the zoom order), the number of horizontal and vertical tiles that you want. You will then pass to the <tt>-g</tt> option 5 integers in the order previously described, for example:
97
+ -g 300,383,7,3,2
98
+ About the zoom level, if the tools tells you 7, you should pass 10 (17 - 7) to the <tt>-g</tt> option.
99
+
100
+ Note that the zoom levels passed to the <tt>-z</tt> argument must be greater or equal to the zoom level passed to the <tt>-g</tt> argument.
101
+
102
+ ====tile_image.rb
103
+ This is to generate tiles from a local image. You will need the RMagick library for it to work. Basically, you should use this tool: http://open.atlas.free.fr/GMapsTransparenciesImgOver.php. You should have an image that will be displayed at the lowest zoom level that you want and use it with this tool. You can translate and scale the image until you are statisfied. When you are, you must click a link to gather information about the transformation needed for your image. You will need to pass this data to the <tt>-p</tt> option of the tool in the following order: X and Y of the top left corner of the map, 17 minus the zoom level, X and Y padding, scale. So for example, you will have the following argument:
104
+ -p 2503,3667,4,241,115,0.8702928870292888
105
+ Again, about the zoom level, if the tool tells you 7, you should pass 10 (17 - 7) to the <tt>-p</tt> option.
106
+
107
+ About the padding, I had a slight problem on Firefox: I needed to substract 10 to the X and Y paddings given by the online tool to have the correct one to pass to the <tt>-p</tt> option. Maybe it has something to do with the 10-pixel bands at the top and left of the page.
108
+
109
+ You should pass as many images to the tool as you have zoom levels in the <tt>-z</tt> argument: Each image will be used for one zoom level. The string order of the names of the image files must match the order of their corresponding zoom level. Moreover, each image should be exactly twice bigger (in both width and height) than the one in the zoom level immediately preceding it. Also note that the zoom levels passed to the <tt>-z</tt> argument must be greater or equal to the zoom level passed to the <tt>-p</tt> argument.
110
+
111
+ By default, the tool will generate transparent borders. You can pass 4 integers (R, G, B and A) from 0 to 255 to the -b option to change that:
112
+ -b 255,123,456,42
113
+
114
+ Here is an example of command line execution using the image at http://open.atlas.free.fr/GMapsTransparenciesImgOver.php. If you place it over the satellite images, you would get the following parameters: 2503,3667,13,149,76,1.0 (I have accounted for both the reversing of the zoom for v2 and the 10-pixel drift of the padding in Firefox). I have also created another version of the image, at double the size in width and height. Normally you should do the opposite: Get the highest resolution possible (if you scan the map) and downsize it for all the desired zoom levels until you get the image to use in this online tool (lowest zoom level). If you use vector graphics, you can easily create versions at different resolutions. I placed the 2 images (called MAP_zoom1.jpg for the smallest and MAP_zoom2.jpg for the biggest) in directory <tt>./input</tt>. And I launch the tool like this:
115
+ ruby tile_image.rb -o ./tiles -z 13..14 -p 2503,3667,13,149,76,1.0 ./input/*
116
+ This will create tiles of the image for zoom levels 13 and 14. It should take just a few seconds.
314
117
 
315
118
  ==Changes since last version
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
119
+ - Repackaging of the Google Maps web app related functionnalities to a separate plugin
120
+ - Suppression of support for the JS Flash API, since it was limited anyway and has not been updated in a while.
319
121
 
320
122
  ==TODO
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.
323
- - Tutorials
123
+ - Add more tests
324
124
 
325
125
  ==Disclaimer
326
126
  This software is not endorsed in any way by Yahoo! or Google.
327
127
 
328
- ==Acknowledgement
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).
330
-
331
128
  ==License
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.
129
+ YM4R is released under the MIT license.
333
130
 
334
131
  ==Support
335
132
  Any questions, enhancement proposals, bug notifications or corrections can be sent to mailto:guilhem.vellut+ym4r@gmail.com.
@@ -1,8 +1,3 @@
1
- require 'ym4r/google_maps/mapping'
2
- require 'ym4r/google_maps/map'
3
- require 'ym4r/google_maps/control'
4
- require 'ym4r/google_maps/overlay'
5
- require 'ym4r/google_maps/point'
6
- require 'ym4r/google_maps/api_key'
7
- require 'ym4r/google_maps/layer.rb'
8
- require 'ym4r/google_maps/helper'
1
+ require 'ym4r/google_maps/geocoding'
2
+ require 'ym4r/google_maps/image_tiler'
3
+ require 'ym4r/google_maps/wms_tiler'
@@ -1 +1,80 @@
1
- require 'ym4r/google_maps/geocoding/geocoding'
1
+ require 'ym4r/google_maps/api_key'
2
+ require 'open-uri'
3
+ require 'rexml/document'
4
+
5
+ module Ym4r
6
+ module GoogleMaps
7
+ module Geocoding
8
+
9
+ GEO_SUCCESS = 200
10
+ GEO_MISSING_ADDRESS = 601
11
+ GEO_UNKNOWN_ADDRESS = 602
12
+ GEO_UNAVAILABLE_ADDRESS = 603
13
+ GEO_BAD_KEY = 610
14
+ GEO_TOO_MANY_QUERIES = 620
15
+ GEO_SERVER_ERROR = 500
16
+
17
+ #Gets placemarks by querying the Google Maps Geocoding service with the +request+ string
18
+ def self.get(request)
19
+
20
+ url = "http://maps.google.com/maps/geo?q=#{request}&key=#{API_KEY}&output=xml"
21
+ begin
22
+ xml = open(URI.encode(url)).read
23
+ rescue
24
+ raise ConnectionException.new("Unable to connect to Google Maps Geocoding service")
25
+ end
26
+
27
+ doc = REXML::Document.new(xml)
28
+
29
+ response = doc.elements['//Response']
30
+ placemarks = Placemarks.new(response.elements['name'].text,response.elements['Status/code'].text.to_i)
31
+ response.elements.each("Placemark") do |placemark|
32
+ data = placemark.elements
33
+ data_country = data['//CountryNameCode']
34
+ data_administrative = data['//AdministrativeAreaName']
35
+ data_sub_administrative = data['//SubAdministrativeAreaName']
36
+ data_locality = data['//LocalityName']
37
+ data_dependent_locality = data['//DependentLocalityName']
38
+ data_thoroughfare = data['//ThoroughfareName']
39
+ data_postal_code = data['//PostalCodeNumber']
40
+ placemarks << Geocoding::Placemark.new(data['address'].text,
41
+ data_country.nil? ? "" : data_country.text,
42
+ data_administrative.nil? ? "" : data_administrative.text,
43
+ data_sub_administrative.nil? ? "" : data_sub_administrative.text,
44
+ data_locality.nil? ? "" : data_locality.text,
45
+ data_dependent_locality.nil? ? "" : data_dependent_locality.text,
46
+ data_thoroughfare.nil? ? "" : data_thoroughfare.text,
47
+ data_postal_code.nil? ? "" : data_postal_code.text,
48
+ *(data['//coordinates'].text.split(",")[0..1].collect {|l| l.to_f }))
49
+ end
50
+ placemarks
51
+ end
52
+
53
+ #Group of placemarks returned by the Geocoding service. If the result is valid the +statius+ attribute should be equal to <tt>Geocoding::GEI_SUCCESS</tt>
54
+ class Placemarks < Array
55
+ attr_accessor :name,:status
56
+
57
+ def initialize(name,status)
58
+ super(0)
59
+ @name = name
60
+ @status = status
61
+ end
62
+ end
63
+
64
+ #A result from the Geocoding service.
65
+ class Placemark < Struct.new(:address,:country_code,:administrative_area,:sub_administrative_area,:locality,:dependent_locality,:thoroughfare,:postal_code,:longitude,:latitude)
66
+ def lonlat
67
+ [longitude,latitude]
68
+ end
69
+
70
+ def latlon
71
+ [latitude,longitude]
72
+ end
73
+ end
74
+
75
+ #Raised when the connection to the service is not possible
76
+ class ConnectionException < StandardError
77
+ end
78
+ end
79
+ end
80
+ end
@@ -1,2 +1 @@
1
1
  require 'ym4r/yahoo_maps/building_block'
2
- require 'ym4r/yahoo_maps/flash'
data/rakefile.rb CHANGED
@@ -24,8 +24,8 @@ spec = Gem::Specification::new do |s|
24
24
  s.platform = Gem::Platform::RUBY
25
25
 
26
26
  s.name = 'ym4r'
27
- s.version = "0.4.1"
28
- s.summary = "Using Google Maps and Yahoo! Maps from Ruby and Rails"
27
+ s.version = "0.5.1"
28
+ s.summary = "Helping the use of Google Maps and Yahoo! Maps API's from Ruby and Rails"
29
29
  s.description = <<EOF
30
30
  EOF
31
31
  s.author = 'Guilhem Vellut'
@@ -34,7 +34,7 @@ EOF
34
34
 
35
35
  s.requirements << 'none'
36
36
  s.require_path = 'lib'
37
- s.files = FileList["lib/**/*.rb", "lib/**/*.yml","lib/**/*.js","tools/**/*.rb","test/**/*.rb", "README","MIT-LICENSE","rakefile.rb"]
37
+ s.files = FileList["lib/**/*.rb", "lib/**/*.yml","tools/**/*.rb","test/**/*.rb", "README","MIT-LICENSE","rakefile.rb"]
38
38
  s.test_files = FileList['test/test*.rb']
39
39
 
40
40
  s.has_rdoc = true
@@ -8,7 +8,7 @@ include Ym4r::YahooMaps::BuildingBlock
8
8
  class TestGeocoding< Test::Unit::TestCase
9
9
 
10
10
  def test_apple
11
- results1 = Geocoding::get(:street => "1 Infinite Loop",
11
+ results1 = Ym4r::YahooMaps::BuildingBlock::Geocoding::get(:street => "1 Infinite Loop",
12
12
  :city => "Cupertino",
13
13
  :state => "CA",
14
14
  :zip => "95014")
@@ -17,7 +17,7 @@ class TestGeocoding< Test::Unit::TestCase
17
17
  assert(result1.exact_match?)
18
18
  assert_equal("address",result1.precision)
19
19
 
20
- results2 = Geocoding::get(:location => "1 Infinite Loop Cupertino CA 95014")
20
+ results2 = Ym4r::YahooMaps::BuildingBlock::Geocoding::get(:location => "1 Infinite Loop Cupertino CA 95014")
21
21
  assert_equal(1,results2.length)
22
22
  result2 = results2[0]
23
23
  assert(result2.exact_match?)
@@ -27,11 +27,11 @@ class TestGeocoding< Test::Unit::TestCase
27
27
  end
28
28
 
29
29
  def test_garbage_location
30
- assert_raise(BadRequestException) {Geocoding::get(:location => "AZEAEAEAEAEAE")}
30
+ assert_raise(BadRequestException) {Ym4r::YahooMaps::BuildingBlock::Geocoding::get(:location => "AZEAEAEAEAEAE")}
31
31
  end
32
32
 
33
33
  def test_no_location
34
- assert_raise(MissingParameterException) {Geocoding::get(:hello => "world")}
34
+ assert_raise(MissingParameterException) {Ym4r::YahooMaps::BuildingBlock::Geocoding::get(:hello => "world")}
35
35
  end
36
36
 
37
37
  end