ym4r 0.1.4 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README +85 -25
- data/lib/ym4r/google_maps/javascript/wms-gs.js +69 -0
- data/lib/ym4r/google_maps/layer.rb +108 -0
- data/lib/ym4r/google_maps/map.rb +39 -17
- data/lib/ym4r/google_maps/mapping.rb +43 -10
- data/lib/ym4r/google_maps/overlay.rb +11 -16
- data/lib/ym4r/google_maps/tiler/image_tiler.rb +107 -0
- data/lib/ym4r/google_maps/tiler/wms_tiler.rb +157 -0
- data/lib/ym4r/google_maps.rb +1 -0
- data/rakefile.rb +2 -2
- data/test/test_google_maps.rb +44 -9
- data/tools/tile_image.rb +66 -0
- data/tools/tile_wms.rb +78 -0
- metadata +8 -2
data/README
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
=YM4R
|
2
|
-
This is YM4R 0.1.
|
2
|
+
This is YM4R 0.2.1. The goal of YM4R (which naturally means Yellow Maps For Ruby...) is to ease the use of the Google Maps and Yahoo! Maps API's (including the Geocoding, Map Image, Traffic and Local Search API's) from Ruby and Rails.
|
3
3
|
|
4
|
-
|
5
|
-
|
4
|
+
==Operations
|
5
|
+
===Google Maps
|
6
6
|
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:
|
7
7
|
- http://thepochisuperstarmegashow.com/2006/06/02/ym4r-georuby-spatial-adapter-demo/
|
8
8
|
- http://thepochisuperstarmegashow.com/2006/06/03/google-maps-yahoo-traffic-mash-up/
|
9
9
|
Following is some notes about using the library:
|
10
10
|
|
11
|
-
|
11
|
+
====Naming conventions
|
12
12
|
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.
|
13
13
|
|
14
14
|
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.
|
15
15
|
|
16
|
-
|
17
|
-
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
|
16
|
+
====Binding JavaScript and Ruby
|
17
|
+
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.
|
18
18
|
|
19
19
|
For example, here is a typical initialization sequence for a map
|
20
20
|
@map = GMap.new("map_div")
|
@@ -29,32 +29,91 @@ Starting with version 0.1.4 of the YM4R library, this call to +record_init+ to a
|
|
29
29
|
which is strictly equivalent to
|
30
30
|
@map.record_init @map.add_overlay(new GMarker([35.12878, -110.578],:title => "Hello!"))
|
31
31
|
|
32
|
-
|
33
|
-
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 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.
|
32
|
+
====Initialization of the map
|
33
|
+
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.
|
34
34
|
|
35
35
|
Then in your template, you have 2 necessary calls:
|
36
36
|
- <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.
|
37
|
-
- <tt>GMap#to_html</tt>: Outputs the initialization code of the map. By default, it outputs the +script+ tags and initializes the map
|
37
|
+
- <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.
|
38
38
|
|
39
|
-
|
39
|
+
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.
|
40
40
|
|
41
|
-
|
41
|
+
====Update of the map
|
42
42
|
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.
|
43
43
|
|
44
44
|
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:
|
45
45
|
@map = Variable.new("map")
|
46
46
|
+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:
|
47
|
-
page
|
47
|
+
page << @map.add_overlay(GMarker.new([123123.1,12313.76],:title => "Hello again!"))
|
48
48
|
What is sent to the browser will be the fllowing JavaScript code:
|
49
49
|
map.addOverlay(new GMarker(new GLatLng(123123.1,12313.76),{title:\"Hello again!\"}))
|
50
50
|
|
51
|
-
====
|
51
|
+
====Adding new map types
|
52
|
+
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).
|
53
|
+
|
54
|
+
For exemple, here is how you would setup layers from a public WMS server of the DMAP team of the American Navy:
|
55
|
+
layer = WMSLayer.new("http://columbo.nrlssc.navy.mil/ogcwms/servlet/WMSServlet/AccuWeather_Maps.wms",
|
56
|
+
"20:3,6:3,0:27,0:29,6:19","",
|
57
|
+
{'prefix' => "Map Copyright 2006", 'copyright_texts'=> ["DMAP"]},
|
58
|
+
true,:mapserver,0..17,0.8,"png")
|
59
|
+
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.
|
60
|
+
|
61
|
+
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.
|
62
|
+
|
63
|
+
<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>
|
64
|
+
|
65
|
+
Here is how to define a pretiled layer:
|
66
|
+
layer = PreTiledLayer.new("http://localhost:3000/tiles",
|
67
|
+
{'prefix' => "Map C 2006", 'copyright_texts' => ["Open Atlas"]},
|
68
|
+
13..13,0.7,"gif")
|
69
|
+
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).
|
70
|
+
|
71
|
+
You can add such a layer to a new map type the following way:
|
72
|
+
map_type = GMapType.new(layer,"My WMS")
|
73
|
+
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:
|
74
|
+
map_type = GMapType.new([GMapType::G_SATELLITE_MAP.get_tile_layers[0],layer,GMapType::G_HYBRID_MAP.get_tile_layers[1]],
|
75
|
+
"Test WMS")
|
76
|
+
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.
|
77
|
+
|
78
|
+
Finally to add a map type to a GMap:
|
79
|
+
@map.map_type_init(map_type)
|
80
|
+
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.
|
81
|
+
|
82
|
+
|
83
|
+
====How to generated tiles for use in pretiled layers
|
84
|
+
|
85
|
+
The YM4R library provides 2 tools to generate tiles. They are in the +tools+ directory of the YM4R distribution.
|
86
|
+
|
87
|
+
=====tile_wms.rb
|
88
|
+
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> options 5 integers in the order previously described, for example:
|
89
|
+
-g 300,383,7,3,2
|
90
|
+
About the zoom level, if the tools tells you 7, you should pass 10 (17 - 7) to the <tt>-g</tt> option.
|
91
|
+
|
92
|
+
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.
|
93
|
+
|
94
|
+
=====tile_image.rb
|
95
|
+
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:
|
96
|
+
-p 2503,3667,4,241,115,0.8702928870292888
|
97
|
+
Again, about the zoom level, if the tool tells you 7, you should pass 10 (17 - 7) to the <tt>-p</tt> option.
|
98
|
+
|
99
|
+
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.
|
100
|
+
|
101
|
+
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.
|
102
|
+
|
103
|
+
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:
|
104
|
+
-b 255,123,456,42
|
105
|
+
|
106
|
+
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:
|
107
|
+
ruby tile_image.rb -o ./tiles -z 13..14 -p 2503,3667,13,149,76,1.0 ./input/*
|
108
|
+
This will create tiles of the image for zoom levels 13 and 14. It should take just a few seconds.
|
109
|
+
|
110
|
+
===Yahoo Maps Building Block API
|
52
111
|
Building Block API's (Geocoding, Map Image, Traffic and Local Search) are supported. You have to pass to the +get+ method of the module a hash whose keys are a rubyfied version of the request parameters detailed in the documentation for these API's. You get back a ruby object, with accessors that let you get the returned data in a easy way. You get an exception if not all the parameters have been passed, if the connection to the service could not be made or if the parameters you have passed are of the incorrect value.
|
53
112
|
|
54
113
|
To know what parameters to pass to the +get+ methods and what results you should expect, you should consult the documentation for the building block API's on Yahoo!'s website : http://developer.yahoo.com/maps/index.html#mapsBuildingBlocks .
|
55
114
|
|
56
115
|
Here are some examples and notes about using the different Building Block API's.
|
57
|
-
|
116
|
+
====Geocoding
|
58
117
|
Here is an example of request:
|
59
118
|
require 'ym4r'
|
60
119
|
include Ym4r::BuildingBlock
|
@@ -64,7 +123,7 @@ Here is an example of request:
|
|
64
123
|
:zip => "95014")
|
65
124
|
+results+ is an array of Geocoding::Result objects. The API can return up to 50 results for one request. You can access the data in the result with attributes which are rubyfied versions of the XML elements forming the answer from the service: See the Yahoo! Geocoding documentation to know what these are and their meanings.
|
66
125
|
|
67
|
-
|
126
|
+
====Map Image
|
68
127
|
Here is an example of request:
|
69
128
|
require 'ym4r'
|
70
129
|
include Ym4r::BuildingBlock
|
@@ -75,7 +134,7 @@ Here is an example of request:
|
|
75
134
|
:image_type => "png")
|
76
135
|
+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.
|
77
136
|
|
78
|
-
|
137
|
+
====Traffic
|
79
138
|
Here is an example of request:
|
80
139
|
require 'ym4r'
|
81
140
|
include Ym4r::BuildingBlock
|
@@ -86,7 +145,7 @@ Here is an example of request:
|
|
86
145
|
:include_map => true)
|
87
146
|
+results+ is a Traffic::ResultSet object (subclass of +Array+), containing Traffic::Result objects, each containing information about one traffic incident.
|
88
147
|
|
89
|
-
|
148
|
+
====Local Search
|
90
149
|
Here is an example of request:
|
91
150
|
require 'ym4r'
|
92
151
|
include Ym4r::BuildingBlock
|
@@ -97,23 +156,24 @@ Here is an example of request:
|
|
97
156
|
:query => "chinese")
|
98
157
|
+results+ is a LocalSearch::ResultSet object (subclass of +Array+), containing LocalSearch::Result objects, each containing information about one hit on the Yahoo! local search.
|
99
158
|
|
100
|
-
|
159
|
+
===Yahoo! Maps JS-Flash API
|
101
160
|
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.
|
102
161
|
|
103
|
-
|
104
|
-
- Addition of support for
|
105
|
-
- Addition of
|
162
|
+
==Changes since last version
|
163
|
+
- Addition of support for user-defined layers (WMS and pretiled layers)
|
164
|
+
- Addition of tools to generate tiles to be used with the pretiled layers
|
106
165
|
|
107
|
-
|
166
|
+
==TODO
|
167
|
+
- Add support for easy manipulation of external Google Maps-related libraries: Advanced tooltip manipulation, GeoRSS, Clusterer...
|
108
168
|
- Documentation! Documentation! Documentation!
|
109
169
|
- Tutorials
|
110
170
|
- Finish the support for the Yahoo! Maps JS-Flash API
|
111
171
|
|
112
|
-
|
172
|
+
==Disclaimer
|
113
173
|
This software is not endorsed in any way by Yahoo! or Google.
|
114
174
|
|
115
|
-
|
175
|
+
==License
|
116
176
|
YM4R is released under the MIT license.
|
117
177
|
|
118
|
-
|
178
|
+
==Support
|
119
179
|
Any questions, enhancement proposals, bug notifications or corrections can be sent to mailto:guilhem.vellut+ym4r@gmail.com.
|
@@ -0,0 +1,69 @@
|
|
1
|
+
/*
|
2
|
+
* Call generic wms service for GoogleMaps v2
|
3
|
+
* John Deck, UC Berkeley
|
4
|
+
* Inspiration & Code from:
|
5
|
+
* Mike Williams http://www.econym.demon.co.uk/googlemaps2/ V2 Reference & custommap code
|
6
|
+
* Brian Flood http://www.spatialdatalogic.com/cs/blogs/brian_flood/archive/2005/07/11/39.aspx V1 WMS code
|
7
|
+
* Kyle Mulka http://blog.kylemulka.com/?p=287 V1 WMS code modifications
|
8
|
+
* http://search.cpan.org/src/RRWO/GPS-Lowrance-0.31/lib/Geo/Coordinates/MercatorMeters.pm
|
9
|
+
*
|
10
|
+
* Modified by Chris Holmes, TOPP to work by default with GeoServer.
|
11
|
+
*
|
12
|
+
* Bundled with YM4R with John Deck's permission.
|
13
|
+
* Slightly modified to fit YM4R.
|
14
|
+
* See johndeck.blogspot.com for the original version and for examples and instructions of how to use it.
|
15
|
+
*/
|
16
|
+
|
17
|
+
var WGS84_SEMI_MAJOR_AXIS = 6378137.0; //equatorial radius
|
18
|
+
var WGS84_ECCENTRICITY = 0.0818191913108718138;
|
19
|
+
var DEG2RAD=0.0174532922519943;
|
20
|
+
var PI=3.14159267;
|
21
|
+
|
22
|
+
function dd2MercMetersLng(p_lng) {
|
23
|
+
return WGS84_SEMI_MAJOR_AXIS * (p_lng*DEG2RAD);
|
24
|
+
}
|
25
|
+
|
26
|
+
function dd2MercMetersLat(p_lat) {
|
27
|
+
var lat_rad = p_lat * DEG2RAD;
|
28
|
+
return WGS84_SEMI_MAJOR_AXIS * Math.log(Math.tan((lat_rad + PI / 2) / 2) * Math.pow( ((1 - WGS84_ECCENTRICITY * Math.sin(lat_rad)) / (1 + WGS84_ECCENTRICITY * Math.sin(lat_rad))), (WGS84_ECCENTRICITY/2)));
|
29
|
+
}
|
30
|
+
|
31
|
+
function addWMSPropertiesToLayer(tile_layer,base_url,layers,styles,format,merc_proj,use_geo){
|
32
|
+
tile_layer.format = format;
|
33
|
+
tile_layer.baseURL = base_url;
|
34
|
+
tile_layer.styles = styles;
|
35
|
+
tile_layer.layers = layers;
|
36
|
+
tile_layer.mercatorEpsg = merc_proj;
|
37
|
+
tile_layer.useGeographic = use_geo;
|
38
|
+
return tile_layer;
|
39
|
+
}
|
40
|
+
|
41
|
+
getTileUrlForWMS=function(a,b,c) {
|
42
|
+
var lULP = new GPoint(a.x*256,(a.y+1)*256);
|
43
|
+
var lLRP = new GPoint((a.x+1)*256,a.y*256);
|
44
|
+
var lUL = G_NORMAL_MAP.getProjection().fromPixelToLatLng(lULP,b,c);
|
45
|
+
var lLR = G_NORMAL_MAP.getProjection().fromPixelToLatLng(lLRP,b,c);
|
46
|
+
|
47
|
+
if (this.useGeographic){
|
48
|
+
var lBbox=lUL.x+","+lUL.y+","+lLR.x+","+lLR.y;
|
49
|
+
var lSRS="EPSG:4326";
|
50
|
+
}else{
|
51
|
+
var lBbox=dd2MercMetersLng(lUL.x)+","+dd2MercMetersLat(lUL.y)+","+dd2MercMetersLng(lLR.x)+","+dd2MercMetersLat(lLR.y);
|
52
|
+
var lSRS="EPSG:" + this.mercatorEpsg;
|
53
|
+
}
|
54
|
+
var lURL=this.baseURL;
|
55
|
+
lURL+="?REQUEST=GetMap";
|
56
|
+
lURL+="&SERVICE=WMS";
|
57
|
+
lURL+="&VERSION=1.1.1";
|
58
|
+
lURL+="&LAYERS="+this.layers;
|
59
|
+
lURL+="&STYLES="+this.styles;
|
60
|
+
lURL+="&FORMAT=image/"+this.format;
|
61
|
+
lURL+="&BGCOLOR=0xFFFFFF";
|
62
|
+
lURL+="&TRANSPARENT=TRUE";
|
63
|
+
lURL+="&SRS="+lSRS;
|
64
|
+
lURL+="&BBOX="+lBbox;
|
65
|
+
lURL+="&WIDTH=256";
|
66
|
+
lURL+="&HEIGHT=256";
|
67
|
+
lURL+="&reaspect=false";
|
68
|
+
return lURL;
|
69
|
+
}
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module Ym4r
|
2
|
+
module GoogleMaps
|
3
|
+
#Map types of the map
|
4
|
+
class GMapType
|
5
|
+
include MappingObject
|
6
|
+
|
7
|
+
G_NORMAL_MAP = Variable.new("G_NORMAL_MAP")
|
8
|
+
G_SATELLITE_MAP = Variable.new("G_SATELLITE_MAP")
|
9
|
+
G_HYBRID_MAP = Variable.new("G_HYBRID_MAP")
|
10
|
+
|
11
|
+
attr_accessor :layers, :name, :projection, :options
|
12
|
+
|
13
|
+
def initialize(layers, name, projection = GMercatorProjection.new,options = {})
|
14
|
+
@layers = layers
|
15
|
+
@name = name
|
16
|
+
@projection = projection
|
17
|
+
@options = options
|
18
|
+
end
|
19
|
+
|
20
|
+
def create
|
21
|
+
"new GMapType(#{MappingObject.javascriptify_variable(Array(layers))}, #{MappingObject.javascriptify_variable(projection)}, #{MappingObject.javascriptify_variable(name)}, #{MappingObject.javascriptify_variable(options)})"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class GMercatorProjection
|
26
|
+
include MappingObject
|
27
|
+
|
28
|
+
attr_accessor :n
|
29
|
+
|
30
|
+
def initialize(n = nil)
|
31
|
+
@n = n
|
32
|
+
end
|
33
|
+
|
34
|
+
def create
|
35
|
+
if n.nil?
|
36
|
+
return "G_NORMAL_MAP.getProjection()"
|
37
|
+
else
|
38
|
+
"new GMercatorProjection(#{@n})"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class GTileLayer
|
44
|
+
include MappingObject
|
45
|
+
|
46
|
+
attr_accessor :opacity, :zoom_inter, :copyright
|
47
|
+
|
48
|
+
def initialize(zoom_inter = 0..17, copyright= {'prefix' => '', 'copyright_texts' => [""]}, opacity = 1.0)
|
49
|
+
@opacity = opacity
|
50
|
+
@zoom_inter = zoom_inter
|
51
|
+
@copyright = copyright
|
52
|
+
end
|
53
|
+
|
54
|
+
def create
|
55
|
+
"addPropertiesToLayer(new GTileLayer(new GCopyrightCollection(\"\"),#{zoom_inter.begin},#{zoom_inter.end}),#{get_tile_url},function(a,b) {return #{MappingObject.javascriptify_variable(@copyright)};}\n,function() {return #{@opacity};})"
|
56
|
+
end
|
57
|
+
|
58
|
+
#for subclasses to implement
|
59
|
+
def get_tile_url
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class PreTiledLayer < GTileLayer
|
64
|
+
attr_accessor :base_url, :format
|
65
|
+
|
66
|
+
def initialize(base_url = "http://localhost:8080/tiles", copyright = {'prefix' => '', 'copyright_texts' => [""]}, zoom_inter = 0..17, opacity = 1.0,format = "png")
|
67
|
+
super(zoom_inter, copyright, opacity)
|
68
|
+
@base_url = base_url
|
69
|
+
@format = format
|
70
|
+
end
|
71
|
+
|
72
|
+
#returns the code to determine the url to fetch the tile. Follows the convention adopted by the tiler: {base_url}/tile_{b}_{a.x}_{a.y}.{format}
|
73
|
+
def get_tile_url
|
74
|
+
"function(a,b,c) { return '#{@base_url}/tile_' + b + '_' + a.x + '_' + a.y + '.#{@format}';}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
#needs to include the JavaScript file wms-gs.js for this to work
|
79
|
+
#see http://docs.codehaus.org/display/GEOSDOC/Google+Maps
|
80
|
+
class WMSLayer < GTileLayer
|
81
|
+
attr_accessor :base_url, :layers, :styles, :format, :merc_proj, :use_geographic
|
82
|
+
|
83
|
+
def initialize(base_url, layers, styles = "", copyright = {'prefix' => '', 'copyright_texts' => [""]}, use_geographic = false, merc_proj = :mapserver, zoom_inter = 0..17, opacity = 1.0,format= "png")
|
84
|
+
super(zoom_inter, copyright, opacity)
|
85
|
+
@base_url = base_url
|
86
|
+
@layers = layers
|
87
|
+
@styles = styles
|
88
|
+
@format = format
|
89
|
+
@merc_proj = if merc_proj == :mapserver
|
90
|
+
"54004"
|
91
|
+
elsif merc_proj == :geoserver
|
92
|
+
"41001"
|
93
|
+
else
|
94
|
+
merc_proj.to_s
|
95
|
+
end
|
96
|
+
@use_geographic = use_geographic
|
97
|
+
end
|
98
|
+
|
99
|
+
def get_tile_url
|
100
|
+
"getTileUrlForWMS"
|
101
|
+
end
|
102
|
+
|
103
|
+
def create
|
104
|
+
"addWMSPropertiesToLayer(#{super},#{MappingObject.javascriptify_variable(@base_url)},#{MappingObject.javascriptify_variable(@layers)},#{MappingObject.javascriptify_variable(@styles)},#{MappingObject.javascriptify_variable(@format)},#{MappingObject.javascriptify_variable(@merc_proj)},#{MappingObject.javascriptify_variable(@use_geographic)})"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
data/lib/ym4r/google_maps/map.rb
CHANGED
@@ -15,6 +15,7 @@ module Ym4r
|
|
15
15
|
@container = container
|
16
16
|
@variable = variable
|
17
17
|
@init = []
|
18
|
+
@init_end = [] #for stuff that must be initialized at the end (controls)
|
18
19
|
@global_init = []
|
19
20
|
end
|
20
21
|
|
@@ -24,6 +25,11 @@ module Ym4r
|
|
24
25
|
a << "<style type=\"text/css\">\n v\:* { behavior:url(#default#VML);}\n</style>" if with_vml
|
25
26
|
a
|
26
27
|
end
|
28
|
+
|
29
|
+
#Outputs the <div id=...></div> which has been configured to contain the map
|
30
|
+
def div
|
31
|
+
"<div id=\"#{@container}\"></div>"
|
32
|
+
end
|
27
33
|
|
28
34
|
#Outputs a style declaration setting the dimensions of the DIV container of the map. This info can also be set manually in a CSS.
|
29
35
|
def header_width_height(width,height)
|
@@ -37,12 +43,12 @@ module Ym4r
|
|
37
43
|
|
38
44
|
#Initializes the controls: you can pass a hash with keys <tt>:small_map</tt>, <tt>:large_map</tt>, <tt>:small_zoom</tt>, <tt>:scale</tt>, <tt>:map_type</tt> and <tt>:overview_map</tt> and a boolean value as the value (usually true, since the control is not displayed by default)
|
39
45
|
def control_init(controls = {})
|
40
|
-
@
|
41
|
-
@
|
42
|
-
@
|
43
|
-
@
|
44
|
-
@
|
45
|
-
@
|
46
|
+
@init_end << add_control(GSmallMapControl.new) if controls[:small_map]
|
47
|
+
@init_end << add_control(GLargeMapControl.new) if controls[:large_map]
|
48
|
+
@init_end << add_control(GSmallZoomControl.new) if controls[:small_zoom]
|
49
|
+
@init_end << add_control(GScaleControl.new) if controls[:scale]
|
50
|
+
@init_end << add_control(GMapTypeControl.new) if controls[:map_type]
|
51
|
+
@init_end << add_control(GOverviewMapControl.new) if controls[:overview_map]
|
46
52
|
end
|
47
53
|
|
48
54
|
#Initializes the initial center and zoom of the map. +center+ can be both a GLatLng object or a 2-float array.
|
@@ -54,43 +60,64 @@ module Ym4r
|
|
54
60
|
end
|
55
61
|
end
|
56
62
|
|
57
|
-
#Initializes the map by adding an overlay (marker or polyline).
|
63
|
+
#Initializes the map by adding an overlay (marker or polyline).
|
58
64
|
def overlay_init(overlay)
|
59
65
|
@init << add_overlay(overlay)
|
60
66
|
end
|
61
67
|
|
68
|
+
#Sets up a new map type. If +add+ is false, all the other map types of the map are wiped out. If +set+ is true, the current map type for the map is set to +map_type+. If you want to access the map type in other methods, you should declare the map type first.
|
69
|
+
def map_type_init(map_type, add = true)
|
70
|
+
unless add
|
71
|
+
@init << get_map_types.set_property(:length,0)
|
72
|
+
end
|
73
|
+
@init << add_map_type(map_type)
|
74
|
+
end
|
75
|
+
|
76
|
+
#Locally declare a MappingObject with variable name "name"
|
77
|
+
def declare_init(variable, name)
|
78
|
+
@init << variable.declare(name)
|
79
|
+
end
|
80
|
+
|
62
81
|
#Records arbitrary JavaScript code and outputs it during initialization outside the +load+ function (ie globally).
|
63
82
|
def record_global_init(code)
|
64
83
|
@global_init << code
|
65
84
|
end
|
66
85
|
|
67
86
|
#Initializes an icon and makes it globally accessible through the JavaScript variable of name +variable+.
|
68
|
-
def icon_init(icon ,
|
69
|
-
|
87
|
+
def icon_init(icon , name)
|
88
|
+
declare_global_init(icon,name)
|
89
|
+
end
|
90
|
+
|
91
|
+
#Globally declare a MappingObject with variable name "name"
|
92
|
+
def declare_global_init(variable,name)
|
93
|
+
@global_init << variable.declare(name)
|
70
94
|
end
|
71
95
|
|
72
96
|
#Outputs the initialization code for the map. By default, it outputs the script tags, performs the initialization inside a function called +load+ and makes the map globally available.
|
73
97
|
def to_html(options = {})
|
74
98
|
no_load = options[:no_load]
|
75
|
-
load_method = options[:load_method] || "load"
|
76
99
|
no_script_tag = options[:no_script_tag]
|
77
100
|
no_declare = options[:no_declare]
|
78
101
|
no_global = options[:no_global]
|
79
102
|
|
80
103
|
html = ""
|
81
104
|
html << "<script type=\"text/javascript\">\n" if !no_script_tag
|
105
|
+
#put the functions in a separate javascript file to be included in the page
|
82
106
|
html << "function addInfoWindowToMarker(marker,info){\nGEvent.addListener(marker, \"click\", function() {\nmarker.openInfoWindowHtml(info);\n});\nreturn marker;\n}\n"
|
83
107
|
html << "function addInfoWindowTabsToMarker(marker,info){\nGEvent.addListener(marker, \"click\", function() {\nmarker.openInfoWindowTabsHtml(info);\n});\nreturn marker;\n}\n"
|
108
|
+
html << "function addPropertiesToLayer(layer,getTile,copyright,opacity){\nlayer.getTileUrl = getTile;\nlayer.getCopyright = copyright;\nlayer.getOpacity = opacity;\nreturn layer;\n}\n"
|
84
109
|
html << @global_init * "\n"
|
85
110
|
html << "var #{@variable};\n" if !no_declare and !no_global
|
86
|
-
html << "function
|
111
|
+
html << "window.onload = function() {\nif (GBrowserIsCompatible()) {\n" if !no_load
|
87
112
|
if !no_declare and no_global
|
88
113
|
html << "#{declare(@variable)}\n"
|
89
114
|
else
|
90
115
|
html << "#{assign_to(@variable)}\n"
|
91
116
|
end
|
92
117
|
html << @init * "\n"
|
118
|
+
html << @init_end * "\n"
|
93
119
|
html << "\n}\n}\n" if !no_load
|
120
|
+
html << "window.onunload = GUnload;\n"
|
94
121
|
html << "</script>" if !no_script_tag
|
95
122
|
html
|
96
123
|
end
|
@@ -101,12 +128,7 @@ module Ym4r
|
|
101
128
|
end
|
102
129
|
end
|
103
130
|
|
104
|
-
|
105
|
-
module GMapType
|
106
|
-
G_NORMAL_MAP = Variable.new("GMapType.G_NORMAL_MAP")
|
107
|
-
G_SATELLITE_MAP = Variable.new("GMapType.G_SATELLITE_MAP")
|
108
|
-
G_HYBRID_MAP = Variable.new("GMapType.G_HYBRID_MAP")
|
109
|
-
end
|
131
|
+
|
110
132
|
end
|
111
133
|
end
|
112
134
|
|
@@ -8,42 +8,75 @@ module Ym4r
|
|
8
8
|
#Creates javascript code for missing methods
|
9
9
|
def method_missing(name,*args)
|
10
10
|
args.collect! do |arg|
|
11
|
-
javascriptify_variable(arg)
|
11
|
+
MappingObject.javascriptify_variable(arg)
|
12
12
|
end
|
13
|
-
Variable.new("#{to_javascript}.#{javascriptify_method(name.to_s)}(#{args.join(",")})")
|
13
|
+
Variable.new("#{to_javascript}.#{MappingObject.javascriptify_method(name.to_s)}(#{args.join(",")})")
|
14
|
+
end
|
15
|
+
|
16
|
+
#Creates javascript code for array or hash indexing
|
17
|
+
def [](index) #index could be an integer or string
|
18
|
+
return Variable.new("#{to_javascript}[#{MappingObject.javascriptify_variable(index)}]")
|
14
19
|
end
|
15
20
|
|
16
|
-
#Transforms a Ruby object into a JavaScript string
|
17
|
-
def javascriptify_variable(arg)
|
21
|
+
#Transforms a Ruby object into a JavaScript string : MAppingObject, String, Array, Hash and general case (using to_s)
|
22
|
+
def self.javascriptify_variable(arg)
|
18
23
|
if arg.is_a?(MappingObject)
|
19
24
|
arg.to_javascript
|
20
25
|
elsif arg.is_a?(String)
|
21
|
-
"\"#{escape_javascript(arg)}\""
|
26
|
+
"\"#{MappingObject.escape_javascript(arg)}\""
|
27
|
+
elsif arg.is_a?(Array)
|
28
|
+
"[" + arg.collect{ |a| MappingObject.javascriptify_variable(a)}.join(",") + "]"
|
29
|
+
elsif arg.is_a?(Hash)
|
30
|
+
"{" + arg.to_a.collect do |v|
|
31
|
+
"#{MappingObject.javascriptify_method(v[0].to_s)} : #{MappingObject.javascriptify_variable(v[1])}"
|
32
|
+
end.join(",") + "}"
|
22
33
|
else
|
23
34
|
arg.to_s
|
24
35
|
end
|
25
36
|
end
|
26
37
|
|
27
38
|
#Escape string to be used in JavaScript. Lifted from rails.
|
28
|
-
def escape_javascript(javascript)
|
39
|
+
def self.escape_javascript(javascript)
|
29
40
|
javascript.gsub(/\r\n|\n|\r/, "\\n").gsub(/["']/) { |m| "\\#{m}" }
|
30
41
|
end
|
31
42
|
|
32
43
|
#Transform a ruby-type method name (like add_overlay) to a JavaScript-style one (like addOverlay).
|
33
|
-
def javascriptify_method(method_name)
|
44
|
+
def self.javascriptify_method(method_name)
|
34
45
|
method_name.gsub(/_(\w)/){|s| $1.upcase}
|
35
46
|
end
|
36
47
|
|
37
48
|
#Declares a Mapping Object bound to a JavaScript variable of name +variable+.
|
38
49
|
def declare(variable)
|
39
50
|
@variable = variable
|
40
|
-
"var #{variable} = #{create};"
|
51
|
+
"var #{@variable} = #{create};"
|
52
|
+
end
|
53
|
+
|
54
|
+
#declare with a random variable name
|
55
|
+
def declare_random(init,size = 8)
|
56
|
+
s = init.clone
|
57
|
+
6.times { s << (i = Kernel.rand(62); i += ((i < 10) ? 48 : ((i < 36) ? 55 : 61 ))).chr }
|
58
|
+
declare(s)
|
59
|
+
end
|
60
|
+
|
61
|
+
#Checks if the MappinObject has been declared
|
62
|
+
def declared?
|
63
|
+
!@variable.nil?
|
41
64
|
end
|
42
65
|
|
43
66
|
#Binds a Mapping object to a previously declared JavaScript variable of name +variable+.
|
44
67
|
def assign_to(variable)
|
45
68
|
@variable = variable
|
46
|
-
"#{variable} = #{create};"
|
69
|
+
"#{@variable} = #{create};"
|
70
|
+
end
|
71
|
+
|
72
|
+
#Assign the +value+ to the +property+ of the MappingObject
|
73
|
+
def set_property(property, value)
|
74
|
+
"#{to_javascript}.#{MappingObject.javascriptify_method(property.to_s)} = #{MappingObject.javascriptify_variable(value)}"
|
75
|
+
end
|
76
|
+
|
77
|
+
#Returns the code to get a +property+ from the MappingObject
|
78
|
+
def get_property(property)
|
79
|
+
Variable.new("#{to_javascript}.#{MappingObject.javascriptify_method(property.to_s)}")
|
47
80
|
end
|
48
81
|
|
49
82
|
#Returns a Javascript code representing the object
|
@@ -61,7 +94,7 @@ module Ym4r
|
|
61
94
|
end
|
62
95
|
end
|
63
96
|
|
64
|
-
#Used to bind a ruby variable to an already existing JavaScript one.
|
97
|
+
#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.
|
65
98
|
class Variable
|
66
99
|
include MappingObject
|
67
100
|
def initialize(variable)
|
@@ -20,19 +20,14 @@ module Ym4r
|
|
20
20
|
#Creates a marker: If an info_window or info_window_tabs is present, the response to the click action from the user is setup here.
|
21
21
|
def create
|
22
22
|
if @options.empty?
|
23
|
-
creation = "new GMarker(#{@point
|
23
|
+
creation = "new GMarker(#{MappingObject.javascriptify_variable(@point)})"
|
24
24
|
else
|
25
|
-
|
26
|
-
options << @options.to_a.collect do |v|
|
27
|
-
"#{v[0].to_s} : #{javascriptify_variable(v[1])}"
|
28
|
-
end.join(",")
|
29
|
-
options << "}"
|
30
|
-
creation = "new GMarker(#{@point.to_javascript},#{options})"
|
25
|
+
creation = "new GMarker(#{MappingObject.javascriptify_variable(@point)},#{MappingObject.javascriptify_variable(@options)})"
|
31
26
|
end
|
32
27
|
if @info_window
|
33
|
-
"addInfoWindowToMarker(#{creation},#{javascriptify_variable(@info_window)})"
|
28
|
+
"addInfoWindowToMarker(#{creation},#{MappingObject.javascriptify_variable(@info_window)})"
|
34
29
|
elsif @tab_info_window
|
35
|
-
"addInfoWindowTabsToMarker(#{creation}
|
30
|
+
"addInfoWindowTabsToMarker(#{creation},#{MappingObject.javascriptify_variable(Array(@tab_info_window))})"
|
36
31
|
else
|
37
32
|
creation
|
38
33
|
end
|
@@ -43,7 +38,7 @@ module Ym4r
|
|
43
38
|
class GInfoWindowTab < Struct.new(:tab,:content)
|
44
39
|
include MappingObject
|
45
40
|
def create
|
46
|
-
"new GInfoWindowTab(#{javascriptify_variable(tab)},#{javascriptify_variable(content)})"
|
41
|
+
"new GInfoWindowTab(#{MappingObject.javascriptify_variable(tab)},#{MappingObject.javascriptify_variable(content)})"
|
47
42
|
end
|
48
43
|
end
|
49
44
|
|
@@ -61,7 +56,7 @@ module Ym4r
|
|
61
56
|
#Creates a GIcon.
|
62
57
|
def create
|
63
58
|
if @copy_base
|
64
|
-
"new GIcon(#{@copy_base
|
59
|
+
"new GIcon(#{MappingObject.javascriptify_variable(@copy_base)})"
|
65
60
|
else
|
66
61
|
"new GIcon()"
|
67
62
|
end
|
@@ -70,7 +65,7 @@ module Ym4r
|
|
70
65
|
def declare(variable)
|
71
66
|
decl = super(variable) + "\n"
|
72
67
|
@options.each do |key,value|
|
73
|
-
decl << "#{to_javascript}.#{javascriptify_method(key.to_s)} = #{javascriptify_variable(value)};\n"
|
68
|
+
decl << "#{to_javascript}.#{MappingObject.javascriptify_method(key.to_s)} = #{MappingObject.javascriptify_variable(value)};\n"
|
74
69
|
end
|
75
70
|
decl
|
76
71
|
end
|
@@ -93,10 +88,10 @@ module Ym4r
|
|
93
88
|
end
|
94
89
|
#Creates a new polyline.
|
95
90
|
def create
|
96
|
-
a = "new GPolyline([#{@points.collect{|pt| pt
|
97
|
-
a << ",#{javascriptify_variable(@color)}" if @color
|
98
|
-
a << ",#{javascriptify_variable(@weight)}" if @weight
|
99
|
-
a << ",#{javascriptify_variable(@opacity)}" if @opacity
|
91
|
+
a = "new GPolyline([#{@points.collect{|pt| MappingObject.javascriptify_variable(pt)}.join(",")}]"
|
92
|
+
a << ",#{MappingObject.javascriptify_variable(@color)}" if @color
|
93
|
+
a << ",#{MappingObject.javascriptify_variable(@weight)}" if @weight
|
94
|
+
a << ",#{MappingObject.javascriptify_variable(@opacity)}" if @opacity
|
100
95
|
a << ")"
|
101
96
|
end
|
102
97
|
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'RMagick'
|
2
|
+
|
3
|
+
module Ym4r
|
4
|
+
module GoogleMaps
|
5
|
+
module Tiler
|
6
|
+
module ImageTiler
|
7
|
+
class Point < Struct.new(:x,:y)
|
8
|
+
def -(point)
|
9
|
+
Point.new(x - point.x , y - point.y)
|
10
|
+
end
|
11
|
+
def +(point)
|
12
|
+
Point.new(x + point.x , y + point.y)
|
13
|
+
end
|
14
|
+
def *(scale)
|
15
|
+
Point.new(scale * x,scale * y)
|
16
|
+
end
|
17
|
+
def to_s
|
18
|
+
"Point #{x} #{y}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class TileParam < Struct.new(:ul_corner,:zoom,:padding,:scale)
|
23
|
+
end
|
24
|
+
|
25
|
+
TILE_SIZE = 256
|
26
|
+
|
27
|
+
def self.get_tiles(output_dir, input_files, tile_param, zooms, bg_color = Magick::Pixel.new(255,255,255,0), format = "png")
|
28
|
+
#order the input files: string order.
|
29
|
+
sorted_input_files = input_files.sort
|
30
|
+
|
31
|
+
#Whatever the zoom level, the tiles must cover the same surface : we get the surface of the furthest zoom.
|
32
|
+
furthest_dimension_tiles = get_dimension_tiles(sorted_input_files[0],tile_param)
|
33
|
+
puts furthest_dimension_tiles.to_s
|
34
|
+
|
35
|
+
zooms.each do |zoom|
|
36
|
+
next if zoom < tile_param.zoom
|
37
|
+
return if (input_file = sorted_input_files.shift).nil?
|
38
|
+
|
39
|
+
image = Magick::ImageList::new(input_file)
|
40
|
+
image.scale!(tile_param.scale)
|
41
|
+
image_size = Point.new(image.columns , image.rows)
|
42
|
+
|
43
|
+
factor = 2 ** (zoom - tile_param.zoom)
|
44
|
+
|
45
|
+
#index of the upper left corner for the current zoom
|
46
|
+
start = tile_param.ul_corner * factor
|
47
|
+
dimension_tiles = furthest_dimension_tiles * factor
|
48
|
+
dimension_tiles_pixel = dimension_tiles * TILE_SIZE
|
49
|
+
padding = tile_param.padding * factor
|
50
|
+
|
51
|
+
puts "Padding" + padding.to_s
|
52
|
+
puts "Size " + image_size.to_s
|
53
|
+
puts "Dimension " + dimension_tiles_pixel.to_s
|
54
|
+
|
55
|
+
#create an image at dimension_tiles_pixel ; copy the current image there (a bit inefficient memory wise even if it simplifies )
|
56
|
+
image_with_padding = Magick::Image.new(dimension_tiles_pixel.x, dimension_tiles_pixel.y) do
|
57
|
+
self.background_color = bg_color
|
58
|
+
end
|
59
|
+
|
60
|
+
image_with_padding.import_pixels(padding.x,padding.y,image_size.x,image_size.y,"RGBA",image.export_pixels(0,0,image_size.x,image_size.y,"RGBA"))
|
61
|
+
|
62
|
+
image_with_padding.write(output_dir + "/tile_glob_#{zoom}.png")
|
63
|
+
|
64
|
+
total_tiles = dimension_tiles.x * dimension_tiles.y
|
65
|
+
|
66
|
+
counter = Point.new(0,0)
|
67
|
+
|
68
|
+
cur_tile = Point.new(start.x,start.y)
|
69
|
+
|
70
|
+
1.upto(total_tiles) do |tile|
|
71
|
+
#progress column by column
|
72
|
+
if counter.y == dimension_tiles.y
|
73
|
+
counter.x += 1
|
74
|
+
counter.y = 0
|
75
|
+
cur_tile.x += 1
|
76
|
+
cur_tile.y = start.y
|
77
|
+
end
|
78
|
+
|
79
|
+
pt_nw = counter * TILE_SIZE
|
80
|
+
|
81
|
+
tile_image = Magick::Image.new(TILE_SIZE,TILE_SIZE)
|
82
|
+
tile_image.import_pixels(0,0,TILE_SIZE,TILE_SIZE,"RGBA",image_with_padding.export_pixels(pt_nw.x,pt_nw.y,TILE_SIZE,TILE_SIZE,"RGBA"))
|
83
|
+
tile_image.write("#{output_dir}/tile_#{zoom}_#{cur_tile.x}_#{cur_tile.y}.#{format}")
|
84
|
+
|
85
|
+
counter.y += 1
|
86
|
+
cur_tile.y += 1
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
def self.get_dimension_tiles(file,tile_param)
|
94
|
+
#Get the size of the first input_file
|
95
|
+
image = Magick::ImageList::new(file)
|
96
|
+
image.scale!(tile_param.scale)
|
97
|
+
image_size = Point.new(image.columns , image.rows)
|
98
|
+
puts "ISize " + image_size.to_s
|
99
|
+
ending = tile_param.padding + image_size
|
100
|
+
puts "Ending " + ending.to_s
|
101
|
+
Point.new((ending.x / TILE_SIZE.to_f).ceil,(ending.y / TILE_SIZE.to_f).ceil)
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
|
3
|
+
module Ym4r
|
4
|
+
module GoogleMaps
|
5
|
+
module Tiler
|
6
|
+
module WmsTiler
|
7
|
+
#Contains LatLon coordinates
|
8
|
+
class LatLng < Struct.new(:lat,:lng)
|
9
|
+
end
|
10
|
+
|
11
|
+
#Contain projected coordinates (in pixel or meter)
|
12
|
+
class Point < Struct.new(:x,:y)
|
13
|
+
end
|
14
|
+
|
15
|
+
#Structure that contains configuration data for the WMS tiler
|
16
|
+
class FurthestZoom < Struct.new(:ul_corner, :zoom, :tile_size)
|
17
|
+
end
|
18
|
+
|
19
|
+
#The size of a Google Maps tile. There are square so only one size.
|
20
|
+
TILE_SIZE = 256
|
21
|
+
|
22
|
+
#Defines a Simple Mercator projection for one level of Google Maps zoom.
|
23
|
+
class MercatorProjection
|
24
|
+
DEG_2_RAD = Math::PI / 180
|
25
|
+
WGS84_SEMI_MAJOR_AXIS = 6378137.0
|
26
|
+
WGS84_ECCENTRICITY = 0.0818191913108718138
|
27
|
+
|
28
|
+
attr_reader :zoom, :size, :pixel_per_degree, :pixel_per_radian, :origin
|
29
|
+
|
30
|
+
def initialize(zoom)
|
31
|
+
@zoom = zoom
|
32
|
+
@size = TILE_SIZE * (2 ** zoom)
|
33
|
+
@pixel_per_degree = @size / 360.0
|
34
|
+
@pixel_per_radian = @size / (2 * Math::PI)
|
35
|
+
@origin = Point.new(@size / 2 , @size / 2)
|
36
|
+
end
|
37
|
+
|
38
|
+
def borne(number, inf, sup)
|
39
|
+
if(number < inf)
|
40
|
+
inf
|
41
|
+
elsif(number > sup)
|
42
|
+
sup
|
43
|
+
else
|
44
|
+
number
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
#Transforms LatLon coordinate into pixel coordinates in the Google Maps sense
|
49
|
+
#See http://www.math.ubc.ca/~israel/m103/mercator/mercator.html for details
|
50
|
+
def latlng_to_pixel(latlng)
|
51
|
+
answer = Point.new
|
52
|
+
answer.x = (@origin.x + latlng.lng * @pixel_per_degree).round
|
53
|
+
sin = borne(Math.sin(latlng.lat * DEG_2_RAD),-0.9999,0.9999)
|
54
|
+
answer.y = (@origin.y + 0.5 * Math.log((1 + sin) / (1 - sin)) * -@pixel_per_radian).round
|
55
|
+
answer
|
56
|
+
end
|
57
|
+
|
58
|
+
#Transforms pixel coordinates in the Google Maps sense to LatLon coordinates
|
59
|
+
def pixel_to_latlng(point)
|
60
|
+
answer = LatLng.new
|
61
|
+
lng = (point.x - @origin.x) / @pixel_per_degree;
|
62
|
+
answer.lng = lng - (((lng + 180)/360).round * 360)
|
63
|
+
lat = (2 * Math.atan(Math.exp((point.y - @origin.y) / -@pixel_per_radian))- Math::PI / 2) / DEG_2_RAD
|
64
|
+
answer.lat = borne(lat,-90,90)
|
65
|
+
answer
|
66
|
+
end
|
67
|
+
|
68
|
+
#Projects LatLon coordinates in the WGS84 datum to meter coordinates using the Simple Mercator projection
|
69
|
+
def self.latlng_to_meters(latlng)
|
70
|
+
answer = Point.new
|
71
|
+
answer.x = WGS84_SEMI_MAJOR_AXIS * latlng.lng * DEG_2_RAD
|
72
|
+
lat_rad = latlng.lat * DEG_2_RAD
|
73
|
+
answer.y = WGS84_SEMI_MAJOR_AXIS * Math.log(Math.tan((lat_rad + Math::PI / 2) / 2) * ( (1 - WGS84_ECCENTRICITY * Math.sin(lat_rad)) / (1 + WGS84_ECCENTRICITY * Math.sin(lat_rad))) ** (WGS84_ECCENTRICITY/2))
|
74
|
+
answer
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
#Get tiles from a WMS server
|
79
|
+
def self.get_tiles(output_dir, url, furthest_zoom, zooms, layers, geographic = false, epsg = 54004, styles = "", format = "png")
|
80
|
+
|
81
|
+
unless geographic
|
82
|
+
srs_str = epsg
|
83
|
+
else
|
84
|
+
srs_str = 4326 #Geographic WGS84
|
85
|
+
end
|
86
|
+
|
87
|
+
base_url = url << "?REQUEST=GetMap&SERVICE=WMS&VERSION=1.1&LAYERS=#{layers}&STYLES=#{styles}&BGCOLOR=0xFFFFFF&FORMAT=image/#{format}&TRANSPARENT=TRUE&WIDTH=#{TILE_SIZE}&HEIGHT=#{TILE_SIZE}&SRS=EPSG:#{srs_str}&reaspect=false"
|
88
|
+
|
89
|
+
zooms.each do |zoom|
|
90
|
+
next if zoom < furthest_zoom.zoom
|
91
|
+
|
92
|
+
proj = MercatorProjection.new(zoom)
|
93
|
+
|
94
|
+
#from mapki.com
|
95
|
+
factor = 2 ** (zoom - furthest_zoom.zoom)
|
96
|
+
|
97
|
+
#index of the upper left corner
|
98
|
+
x_start = furthest_zoom.ul_corner.x * factor
|
99
|
+
y_start = furthest_zoom.ul_corner.y * factor
|
100
|
+
|
101
|
+
x_tiles = furthest_zoom.tile_size.x * factor
|
102
|
+
y_tiles = furthest_zoom.tile_size.y * factor
|
103
|
+
|
104
|
+
total_tiles = x_tiles * y_tiles
|
105
|
+
|
106
|
+
x_counter = 0
|
107
|
+
y_counter = 0
|
108
|
+
|
109
|
+
x_tile = x_start
|
110
|
+
y_tile = y_start
|
111
|
+
|
112
|
+
1.upto(total_tiles) do |tile|
|
113
|
+
#progress column by column
|
114
|
+
if y_counter == y_tiles
|
115
|
+
x_counter += 1
|
116
|
+
y_counter = 0
|
117
|
+
x_tile += 1
|
118
|
+
y_tile = y_start
|
119
|
+
end
|
120
|
+
|
121
|
+
pt_sw = Point.new( (x_start + x_counter) * TILE_SIZE, (y_start + (y_counter + 1)) * TILE_SIZE) #y grows southbound
|
122
|
+
pt_ne = Point.new((x_start + (x_counter + 1)) * TILE_SIZE, (y_start + y_counter) * TILE_SIZE)
|
123
|
+
|
124
|
+
ll_sw = proj.pixel_to_latlng(pt_sw)
|
125
|
+
ll_ne = proj.pixel_to_latlng(pt_ne)
|
126
|
+
|
127
|
+
unless geographic
|
128
|
+
pt_sw = MercatorProjection.latlng_to_meters(ll_sw)
|
129
|
+
pt_ne = MercatorProjection.latlng_to_meters(ll_ne)
|
130
|
+
bbox_str = "#{pt_sw.x},#{pt_sw.y},#{pt_ne.x},#{pt_ne.y}"
|
131
|
+
else
|
132
|
+
bbox_str = "#{ll_sw.lon},#{ll_sw.lat},#{ll_ne.lon},#{ll_ne.lat}"
|
133
|
+
end
|
134
|
+
|
135
|
+
request_url = "#{base_url}&BBOX=#{bbox_str}"
|
136
|
+
|
137
|
+
begin
|
138
|
+
open("#{output_dir}/tile_#{zoom}_#{x_tile}_#{y_tile}.#{format}","wb") do |f|
|
139
|
+
f.write open(request_url).read
|
140
|
+
end
|
141
|
+
rescue Exception => e
|
142
|
+
puts e
|
143
|
+
raise
|
144
|
+
end
|
145
|
+
|
146
|
+
y_counter += 1
|
147
|
+
y_tile += 1
|
148
|
+
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
|
data/lib/ym4r/google_maps.rb
CHANGED
data/rakefile.rb
CHANGED
@@ -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.1
|
27
|
+
s.version = "0.2.1"
|
28
28
|
s.summary = "Using Google Maps and Yahoo! Maps from Ruby and Rails"
|
29
29
|
s.description = <<EOF
|
30
30
|
EOF
|
@@ -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","test/**/*.rb", "README","MIT-LICENSE","rakefile.rb"]
|
37
|
+
s.files = FileList["lib/**/*.rb", "lib/**/*.yml","lib/**/*.js","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
|
data/test/test_google_maps.rb
CHANGED
@@ -6,14 +6,49 @@ require 'test/unit'
|
|
6
6
|
include Ym4r::GoogleMaps
|
7
7
|
|
8
8
|
class TestGoogleMaps< Test::Unit::TestCase
|
9
|
-
def
|
10
|
-
|
11
|
-
var = Variable.new("hello")
|
12
|
-
yuo = Variable.new("salam")
|
13
|
-
poi = Variable.new("poi")
|
14
|
-
map.record_init map.add_overlay(GMarker.new([123.5,123.56]))
|
15
|
-
map.record_init map.dummy_method(var.other_dummy_method(yuo.kaka_boudin),poi)
|
16
|
-
map.control_init(:small_map => true)
|
17
|
-
puts map.to_html
|
9
|
+
def test_javascriptify_method
|
10
|
+
assert_equal("addOverlayToHello",MappingObject::javascriptify_method("add_overlay_to_hello"))
|
18
11
|
end
|
12
|
+
|
13
|
+
def test_javascriptify_variable_mapping_object
|
14
|
+
map = GMap.new("div")
|
15
|
+
assert_equal(map.to_javascript,MappingObject::javascriptify_variable(map))
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_javascriptify_variable_numeric
|
19
|
+
assert_equal("123.4",MappingObject::javascriptify_variable(123.4))
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_javascriptify_variable_array
|
23
|
+
map = GMap.new("div")
|
24
|
+
assert_equal("[123.4,#{map.to_javascript},[123.4,#{map.to_javascript}]]",MappingObject::javascriptify_variable([123.4,map,[123.4,map]]))
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_javascriptify_variable_hash
|
28
|
+
map = GMap.new("div")
|
29
|
+
test_str = MappingObject::javascriptify_variable("hello" => map, "chopotopoto" => [123.55,map])
|
30
|
+
assert("{hello : #{map.to_javascript},chopotopoto : [123.55,#{map.to_javascript}]}" == test_str || "{chopotopoto : [123.55,#{map.to_javascript}],hello : #{map.to_javascript}}" == test_str)
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_method_call_on_mapping_object
|
34
|
+
map = GMap.new("div","map")
|
35
|
+
assert_equal("map.addHello(123.4);",map.add_hello(123.4).to_s)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_nested_calls_on_mapping_object
|
39
|
+
gmap = GMap.new("div","map")
|
40
|
+
assert_equal("map.addHello(map.hoYoYo(123.4),map);",gmap.add_hello(gmap.ho_yo_yo(123.4),gmap).to_s)
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_declare_variable_latlng
|
44
|
+
point = GLatLng.new([123.4,123.6])
|
45
|
+
assert_equal("var point = new GLatLng(123.4,123.6);",point.declare("point"))
|
46
|
+
assert_equal("point",point.variable)
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_array_indexing
|
50
|
+
obj = Variable.new("obj")
|
51
|
+
assert_equal("obj[0]",obj[0].variable)
|
52
|
+
end
|
53
|
+
|
19
54
|
end
|
data/tools/tile_image.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
2
|
+
require 'ym4r/google_maps/tiler/image_tiler'
|
3
|
+
include Ym4r::GoogleMaps::Tiler
|
4
|
+
|
5
|
+
require 'optparse'
|
6
|
+
require 'ostruct'
|
7
|
+
|
8
|
+
OptionParser.accept(Range, /(\d+)\.\.(\d+)/) do |range,start,finish|
|
9
|
+
Range.new(start.to_i,finish.to_i)
|
10
|
+
end
|
11
|
+
|
12
|
+
OptionParser.accept(ImageTiler::TileParam, /(\d+),(\d+),(\d+),(\d+),(\d+),([\d.]+)/) do |setting,l_corner, u_corner, zoom, padding_x, padding_y, scale|
|
13
|
+
ImageTiler::TileParam.new(ImageTiler::Point.new(l_corner.to_i,u_corner.to_i),zoom.to_i,ImageTiler::Point.new(padding_x.to_i,padding_y.to_i),scale.to_f)
|
14
|
+
end
|
15
|
+
|
16
|
+
OptionParser.accept(Magick::Pixel,/(\d+),(\d+),(\d+),(\d+)/) do |pixel, r,g,b,a|
|
17
|
+
Magick::Pixel.new(r.to_f,g.to_f,b.to_f,a.to_f)
|
18
|
+
end
|
19
|
+
|
20
|
+
options = OpenStruct.new
|
21
|
+
#set some defaults
|
22
|
+
options.format = "png"
|
23
|
+
options.zoom_range = 0..17
|
24
|
+
options.bg_color = Magick::Pixel.new(255,255,255,255)
|
25
|
+
|
26
|
+
opts = OptionParser.new do |opts|
|
27
|
+
opts.banner = "Image Tiler for Google Maps\nUsage: tile_image.rb [options]\nExample: tile_image.rb -o ./tiles -z 11..12 -p 602,768,11,78,112,1.91827348 ./input_files/*.jpg"
|
28
|
+
opts.separator ""
|
29
|
+
opts.on("-o","--output OUTPUT_DIR","Directory where the tiles will be created") do |dir|
|
30
|
+
options.output_dir = dir
|
31
|
+
end
|
32
|
+
opts.on("-f","--format FORMAT","Image format in which to get the file (gif, jpeg, png...). Is png by default") do |format|
|
33
|
+
options.format = format
|
34
|
+
end
|
35
|
+
opts.on("-z","--zooms ZOOM_RANGE",Range,"Range of zoom values at which the tiles must be generated. Is 0..17 by default") do |range|
|
36
|
+
options.zoom_range = range
|
37
|
+
end
|
38
|
+
opts.on("-p","--tile-param PARAM",ImageTiler::TileParam,"Corner coordinates, furthest zoom level, padding in X and Y, scale") do |tp|
|
39
|
+
options.tile_param = tp
|
40
|
+
end
|
41
|
+
opts.on("-b","--background COLOR",Magick::Pixel,"Background color components. Is fully transparent par default") do |bg|
|
42
|
+
options.bg_color = bg
|
43
|
+
end
|
44
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
45
|
+
puts opts
|
46
|
+
exit
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
opts.parse!(ARGV)
|
51
|
+
|
52
|
+
#test the presence of all the options and exit with an error message
|
53
|
+
error = []
|
54
|
+
error << "No output directory defined (-o,--output)" if options.output_dir.nil?
|
55
|
+
error << "No tile parameter defined (-p,--tile-param)" if options.tile_param.nil?
|
56
|
+
error << "No input files defined" if ARGV.empty?
|
57
|
+
|
58
|
+
unless error.empty?
|
59
|
+
puts error * "\n" + "\n\n"
|
60
|
+
puts opts
|
61
|
+
exit
|
62
|
+
end
|
63
|
+
|
64
|
+
ImageTiler.get_tiles(options.output_dir,ARGV,options.tile_param,options.zoom_range,options.bg_color,options.format)
|
65
|
+
|
66
|
+
|
data/tools/tile_wms.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
2
|
+
require 'ym4r/google_maps/tiler/wms_tiler'
|
3
|
+
include Ym4r::GoogleMaps::Tiler
|
4
|
+
|
5
|
+
require 'optparse'
|
6
|
+
require 'ostruct'
|
7
|
+
|
8
|
+
OptionParser.accept(Range, /(\d+)\.\.(\d+)/) do |range,start,finish|
|
9
|
+
Range.new(start.to_i,finish.to_i)
|
10
|
+
end
|
11
|
+
|
12
|
+
OptionParser.accept(WmsTiler::FurthestZoom, /(\d+),(\d+),(\d+),(\d+),(\d+)/) do |setting,l_corner, u_corner, zoom, width, height|
|
13
|
+
WmsTiler::FurthestZoom.new(WmsTiler::Point.new(l_corner.to_i,u_corner.to_i),zoom.to_i,WmsTiler::Point.new(width.to_i,height.to_i))
|
14
|
+
end
|
15
|
+
|
16
|
+
options = OpenStruct.new
|
17
|
+
#set some defaults
|
18
|
+
options.format = "png"
|
19
|
+
options.zoom_range = 0..17
|
20
|
+
options.styles = ""
|
21
|
+
options.srs = 54004
|
22
|
+
options.geographic = false
|
23
|
+
|
24
|
+
opts = OptionParser.new do |opts|
|
25
|
+
opts.banner = "WMS Tiler for Google Maps\nUsage: tile_wms.rb [options]\nExample: tile_wms.rb -o ./tiles -u http://localhost:8080/geoserver/wms -l \"topp:states\" -z 11..12 -g 602,768,11,3,3"
|
26
|
+
opts.separator ""
|
27
|
+
opts.on("-o","--output OUTPUT_DIR","Directory where the tiles will be created") do |dir|
|
28
|
+
options.output_dir = dir
|
29
|
+
end
|
30
|
+
opts.on("-u","--url WMS_SERVICE","URL to the WMS server") do |url|
|
31
|
+
options.url = url
|
32
|
+
end
|
33
|
+
opts.on("-l","--layers LAYERS","String of comma-separated layer names") do |layers|
|
34
|
+
options.layers = layers
|
35
|
+
end
|
36
|
+
opts.on("-s","--styles STYLES","String of comma-separated style names. Is empty by default") do |styles|
|
37
|
+
options.styles = styles
|
38
|
+
end
|
39
|
+
opts.on("-f","--format FORMAT","Image format in which to get the file (gif, jpeg, png...). Is png by default") do |format|
|
40
|
+
options.format = format
|
41
|
+
end
|
42
|
+
opts.on("-z","--zooms ZOOM_RANGE",Range,"Range of zoom values at which the tiles must be generated. Is 0..17 by default") do |range|
|
43
|
+
options.zoom_range = range
|
44
|
+
end
|
45
|
+
opts.on("-g","--gmap-setting SETTING",WmsTiler::FurthestZoom,"Corner coordinates, furthest zoom level, tile width and height") do |fz|
|
46
|
+
options.furthest_zoom = fz
|
47
|
+
end
|
48
|
+
opts.on("-w","--[no-]geographic","Query the WMS server with LatLon coordinates isntead of using the Mercator projection") do |g|
|
49
|
+
options.geographic = g
|
50
|
+
end
|
51
|
+
opts.on("-e", "--epsg SRS","SRS to query the WMS server. Should be a the SRS id of a Simple Mercator projection. Can vary between WMS servers. Is 54004 (Simple Mercator for Mapserver) by default") do |srs|
|
52
|
+
options.srs = srs
|
53
|
+
|
54
|
+
end
|
55
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
56
|
+
puts opts
|
57
|
+
exit
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
opts.parse!(ARGV)
|
62
|
+
|
63
|
+
#test the presence of all the options and exit with an error message
|
64
|
+
error = []
|
65
|
+
error << "No output directory defined (-o,--output)" if options.output_dir.nil?
|
66
|
+
error << "No WMS URL defined (-u,--url)" if options.url.nil?
|
67
|
+
error << "No Google Maps setting defined (-g,--gmap-setting)" if options.furthest_zoom.nil?
|
68
|
+
error << "No WMS layer defined (-l,--layers)" if options.layers.nil?
|
69
|
+
|
70
|
+
unless error.empty?
|
71
|
+
puts error * "\n" + "\n\n"
|
72
|
+
puts opts
|
73
|
+
exit
|
74
|
+
end
|
75
|
+
|
76
|
+
WmsTiler.get_tiles(options.output_dir,options.url,options.furthest_zoom,options.zoom_range,options.layers,options.geographic,options.srs,options.styles,options.format)
|
77
|
+
|
78
|
+
|
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.1
|
7
|
-
date: 2006-06-
|
6
|
+
version: 0.2.1
|
7
|
+
date: 2006-06-12 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
|
@@ -34,10 +34,13 @@ files:
|
|
34
34
|
- lib/ym4r/google_maps/api_key.rb
|
35
35
|
- lib/ym4r/google_maps/control.rb
|
36
36
|
- lib/ym4r/google_maps/helper.rb
|
37
|
+
- lib/ym4r/google_maps/layer.rb
|
37
38
|
- lib/ym4r/google_maps/map.rb
|
38
39
|
- lib/ym4r/google_maps/mapping.rb
|
39
40
|
- lib/ym4r/google_maps/overlay.rb
|
40
41
|
- lib/ym4r/google_maps/point.rb
|
42
|
+
- lib/ym4r/google_maps/tiler/image_tiler.rb
|
43
|
+
- lib/ym4r/google_maps/tiler/wms_tiler.rb
|
41
44
|
- lib/ym4r/yahoo_maps/app_id.rb
|
42
45
|
- lib/ym4r/yahoo_maps/building_block.rb
|
43
46
|
- lib/ym4r/yahoo_maps/flash.rb
|
@@ -54,6 +57,9 @@ files:
|
|
54
57
|
- lib/ym4r/yahoo_maps/flash/tool.rb
|
55
58
|
- lib/ym4r/yahoo_maps/flash/widget.rb
|
56
59
|
- lib/ym4r/google_maps/config/config.yml
|
60
|
+
- lib/ym4r/google_maps/javascript/wms-gs.js
|
61
|
+
- tools/tile_image.rb
|
62
|
+
- tools/tile_wms.rb
|
57
63
|
- test/test_geocoding.rb
|
58
64
|
- test/test_google_maps.rb
|
59
65
|
- test/test_local_search.rb
|