3 #Representing the Google Maps API class GMap2.
\r
5 include MappingObject
\r
7 #A constant containing the declaration of the VML namespace, necessary to display polylines under IE.
\r
8 VML_NAMESPACE = "xmlns:v=\"urn:schemas-microsoft-com:vml\""
\r
10 #The id of the DIV that will contain the map in the HTML page.
\r
11 attr_reader :container
\r
13 #By default the map in the HTML page will be globally accessible with the name +map+.
\r
14 def initialize(container, variable = "map")
\r
15 @container = container
\r
16 @variable = variable
\r
18 @init_end = [] #for stuff that must be initialized at the end (controls)
\r
19 @init_begin = [] #for stuff that must be initialized at the beginning (center + zoom)
\r
23 #Deprecated. Use the static version instead.
\r
24 def header(with_vml = true)
\r
25 GMap.header(:with_vml => with_vml)
\r
28 #Outputs the header necessary to use the Google Maps API, by including the JS files of the API, as well as a file containing YM4R/GM helper functions. By default, it also outputs a style declaration for VML elements. This default can be overriddent by passing <tt>:with_vml => false</tt> as option to the method. You can also pass a <tt>:host</tt> option in order to select the correct API key for the location where your app is currently running, in case the current environment has multiple possible keys. Usually, in this case, you should pass it <tt>@request.host</tt>. If you have defined only one API key for the current environment, the <tt>:host</tt> option is ignored. Finally you can override all the key settings in the configuration by passing a value to the <tt>:key</tt> key. Finally, you can pass a language for the map type buttons with the <tt>:hl</tt> option (possible values are: Japanese (ja), French (fr), German (de), Italian (it), Spanish (es), Catalan (ca), Basque (eu) and Galician (gl): no values means english)
\r
29 def self.header(options = {})
\r
30 options[:with_vml] = true unless options.has_key?(:with_vml)
\r
32 api_key = ApiKey.get(options)
\r
33 a = "<script src=\"http://maps.google.com/maps?file=api&v=2.x&key=#{api_key}&hl=#{options[:hl]}\" type=\"text/javascript\"></script>\n"
\r
34 a << "<script src=\"/javascripts/ym4r-gm.js\" type=\"text/javascript\"></script>\n" unless options[:without_js]
\r
35 a << "<style type=\"text/css\">\n v\:* { behavior:url(#default#VML);}\n</style>" if options[:with_vml]
\r
39 #Outputs the <div id=...></div> which has been configured to contain the map. You can pass <tt>:width</tt> and <tt>:height</tt> as options to output this in the style attribute of the DIV element (you could also achieve the same effect by putting the dimension info into a CSS or using the instance method GMap#header_width_height). You can aslo pass <tt>:class</tt> to set the classname of the div.
\r
40 def div(options = {})
\r
41 attributes = "id=\"#{@container}\" "
\r
42 if options.has_key?(:height) && options.has_key?(:width)
\r
43 attributes += "style=\"width:#{options.delete(:width)}px;height:#{options.delete(:height)}px\" "
\r
45 if options.has_key?(:class)
\r
46 attributes += options.keys.map {|opt| "#{opt}=\"#{options[opt]}\"" }.join(" ")
\r
48 "<div #{attributes}></div>"
\r
51 #Outputs a style declaration setting the dimensions of the DIV container of the map. This info can also be set manually in a CSS.
\r
52 def header_width_height(width,height)
\r
53 "<style type=\"text/css\">\n##{@container} { height: #{height}px;\n width: #{width}px;\n}\n</style>"
\r
56 #Records arbitrary JavaScript code and outputs it during initialization inside the +load+ function.
\r
57 def record_init(code)
\r
61 #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)
\r
62 def control_init(controls = {})
\r
63 @init_end << add_control(GSmallMapControl.new) if controls[:small_map]
\r
64 @init_end << add_control(GLargeMapControl.new) if controls[:large_map]
\r
65 @init_end << add_control(GSmallZoomControl.new) if controls[:small_zoom]
\r
66 @init_end << add_control(GScaleControl.new) if controls[:scale]
\r
67 @init_end << add_control(GMapTypeControl.new) if controls[:map_type]
\r
68 @init_end << add_control(GOverviewMapControl.new) if controls[:overview_map]
\r
71 #Initializes the interface configuration: double-click zoom, dragging, continuous zoom,... You can pass a hash with keys <tt>:dragging</tt>, <tt>:info_window</tt>, <tt>:double_click_zoom</tt>, <tt>:continuous_zoom</tt> and <tt>:scroll_wheel_zoom</tt>. The values should be true or false. Check the google maps API doc to know what the default values are.
\r
72 def interface_init(interface = {})
\r
73 if !interface[:dragging].nil?
\r
74 if interface[:dragging]
\r
75 @init << enableDragging()
\r
77 @init << disableDragging()
\r
80 if !interface[:info_window].nil?
\r
81 if interface[:info_window]
\r
82 @init << enableInfoWindow()
\r
84 @init << disableInfoWindow()
\r
87 if !interface[:double_click_zoom].nil?
\r
88 if interface[:double_click_zoom]
\r
89 @init << enableDoubleClickZoom()
\r
91 @init << disableDoubleClickZoom()
\r
94 if !interface[:continuous_zoom].nil?
\r
95 if interface[:continuous_zoom]
\r
96 @init << enableContinuousZoom()
\r
98 @init << disableContinuousZoom()
\r
101 if !interface[:scroll_wheel_zoom].nil?
\r
102 if interface[:scroll_wheel_zoom]
\r
103 @init << enableScrollWheelZoom()
\r
105 @init << disableScrollWheelZoom()
\r
110 #Initializes the initial center and zoom of the map. +center+ can be both a GLatLng object or a 2-float array.
\r
111 def center_zoom_init(center, zoom)
\r
112 if center.is_a?(GLatLng)
\r
113 @init_begin << set_center(center,zoom)
\r
115 @init_begin << set_center(GLatLng.new(center),zoom)
\r
119 #Center and zoom based on the coordinates passed as argument (either 2D arrays or GLatLng objects)
\r
120 def center_zoom_on_points_init(*points)
\r
121 if(points.length > 0)
\r
122 if(points[0].is_a?(Array))
\r
123 points = points.collect { |point| GLatLng.new(point) }
\r
125 @init_begin << center_and_zoom_on_points(points)
\r
129 #Center and zoom based on the bbox corners. Pass a GLatLngBounds object, an array of 2D coordinates (sw and ne) or an array of GLatLng objects (sw and ne).
\r
130 def center_zoom_on_bounds_init(latlngbounds)
\r
131 if(latlngbounds.is_a?(Array))
\r
132 if latlngbounds[0].is_a?(Array)
\r
133 latlngbounds = GLatLngBounds.new(GLatLng.new(latlngbounds[0]),GLatLng.new(latlngbounds[1]))
\r
134 elsif latlngbounds[0].is_a?(GLatLng)
\r
135 latlngbounds = GLatLngBounds.new(*latlngbounds)
\r
138 #else it is already a latlngbounds object
\r
140 @init_begin << center_and_zoom_on_bounds(latlngbounds)
\r
143 #Initializes the map by adding an overlay (marker or polyline).
\r
144 def overlay_init(overlay)
\r
145 @init << add_overlay(overlay)
\r
148 #Sets up a new map type. If +add+ is false, all the other map types of the map are wiped out. If you want to access the map type in other methods, you should declare the map type first (with +declare_init+).
\r
149 def add_map_type_init(map_type, add = true)
\r
151 @init << get_map_types.set_property(:length,0)
\r
153 @init << add_map_type(map_type)
\r
155 #for legacy purpose
\r
156 alias :map_type_init :add_map_type_init
\r
158 #Sets the map type displayed by default after the map is loaded. It should be known from the map (ie either the default map types or a user-defined map type added with <tt>add_map_type_init</tt>). Use <tt>set_map_type_init(GMapType::G_SATELLITE_MAP)</tt> or <tt>set_map_type_init(GMapType::G_HYBRID_MAP)</tt> to initialize the map with repsecitvely the Satellite view and the hybrid view.
\r
159 def set_map_type_init(map_type)
\r
160 @init << set_map_type(map_type)
\r
163 #Locally declare a MappingObject with variable name "name"
\r
164 def declare_init(variable, name)
\r
165 @init << variable.declare(name)
\r
168 #Records arbitrary JavaScript code and outputs it during initialization outside the +load+ function (ie globally).
\r
169 def record_global_init(code)
\r
170 @global_init << code
\r
173 #Deprecated. Use icon_global_init instead.
\r
174 def icon_init(icon , name)
\r
175 icon_global_init(icon , name)
\r
178 #Initializes an icon and makes it globally accessible through the JavaScript variable of name +variable+.
\r
179 def icon_global_init(icon , name, options = {})
\r
180 declare_global_init(icon,name,options)
\r
183 #Registers an event
\r
184 def event_init(object,event,callback)
\r
185 @init << "GEvent.addListener(#{object.to_javascript},\"#{MappingObject.javascriptify_method(event.to_s)}\",#{callback});"
\r
188 #Registers an event globally
\r
189 def event_global_init(object,event,callback)
\r
190 @global_init << "GEvent.addListener(#{object.to_javascript},\"#{MappingObject.javascriptify_method(event.to_s)}\",#{callback});"
\r
193 #Declares the overlay globally with name +name+
\r
194 def overlay_global_init(overlay,name, options = {})
\r
195 declare_global_init(overlay,name, options)
\r
196 @init << add_overlay(overlay)
\r
199 #Globally declare a MappingObject with variable name "name". Option <tt>:local_construction</tt> should be passed if the construction has to be done inside the onload callback method (for exsample if it depends on the GMap to be initialized)
\r
200 def declare_global_init(variable,name, options = {})
\r
201 unless options[:local_construction]
\r
202 @global_init << "var #{variable.assign_to(name)}"
\r
204 @global_init << "var #{name};"
\r
205 @init << variable.assign_to(name)
\r
209 #Outputs the initialization code for the map. By default, it outputs the script tags, performs the initialization in response to the onload event of the window and makes the map globally available. If you pass +true+ to the option key <tt>:full</tt>, the map will be setup in full screen, in which case it is not necessary (but not harmful) to set a size for the map div.
\r
210 def to_html(options = {})
\r
211 no_load = options[:no_load]
\r
212 no_script_tag = options[:no_script_tag]
\r
213 no_declare = options[:no_declare]
\r
214 no_global = options[:no_global]
\r
215 fullscreen = options[:full]
\r
216 load_pr = options[:proto_load] #to prevent some problems when the onload event callback from Prototype is used
\r
219 html << "<script type=\"text/javascript\">\n" if !no_script_tag
\r
220 #put the functions in a separate javascript file to be included in the page
\r
221 html << @global_init * "\n"
\r
222 html << "var #{@variable};\n" if !no_declare and !no_global
\r
225 html << "Event.observe(window,'load',"
\r
227 html << "window.onload = addCodeToFunction(window.onload,"
\r
229 html << "function() {\n"
\r
232 html << "if (GBrowserIsCompatible()) {\n"
\r
235 #Adding the initial resizing and setting up the event handler for
\r
237 html << "setWindowDims(document.getElementById('#{@container}'));\n"
\r
238 html << "if (window.attachEvent) { window.attachEvent(\"onresize\", function() {setWindowDims(document.getElementById('#{@container}'));})} else {window.addEventListener(\"resize\", function() {setWindowDims(document.getElementById('#{@container}')); } , false);}\n"
\r
241 if !no_declare and no_global
\r
242 html << "#{declare(@variable)}\n"
\r
244 html << "#{assign_to(@variable)}\n"
\r
246 html << @init_begin * "\n"
\r
247 html << @init * "\n"
\r
248 html << @init_end * "\n"
\r
250 html << "});\n" if !no_load
\r
251 html << "</script>" if !no_script_tag
\r
254 #setting up the style in case of full screen
\r
255 html << "<style>html, body {width: 100%; height: 100%} body {margin-top: 0px; margin-right: 0px; margin-left: 0px; margin-bottom: 0px} ##{@container} {margin: 0px;} </style>"
\r
261 #Outputs in JavaScript the creation of a GMap2 object
\r
263 "new GMap2(document.getElementById(\"#{@container}\"))"
\r