diff --git a/gmaps_api_key.yml.sample b/gmaps_api_key.yml.sample index c4cb457..b4114dd 100644 --- a/gmaps_api_key.yml.sample +++ b/gmaps_api_key.yml.sample @@ -1,14 +1,14 @@ -#Fill here the Google Maps API keys for your application -#In this sample: -#For development and test, we have only one possible host (localhost:3000), so there is only a single key associated with the mode. -#In production, the app can be accessed through 2 different hosts: thepochisuperstarmegashow.com and exmaple.com. There then needs a 2-key hash. If you deployed to one host, only the API key would be needed (as in development and test). - -development: - ABQIAAAAzMUFFnT9uH0xq39J0Y4kbhTJQa0g3IQ9GZqIMmInSLzwtGDKaBR6j135zrztfTGVOm2QlWnkaidDIQ - -test: - ABQIAAAAzMUFFnT9uH0xq39J0Y4kbhTJQa0g3IQ9GZqIMmInSLzwtGDKaBR6j135zrztfTGVOm2QlWnkaidDIQ - -production: - thepochisuperstarmegashow.com: ABQIAAAAzMUFFnT9uH0Sfg76Y4kbhTJQa0g3IQ9GZqIMmInSLzwtGDmlRT6e90j135zat56yhJKQlWnkaidDIQ +#Fill here the Google Maps API keys for your application +#In this sample: +#For development and test, we have only one possible host (localhost:3000), so there is only a single key associated with the mode. +#In production, the app can be accessed through 2 different hosts: thepochisuperstarmegashow.com and exmaple.com. There then needs a 2-key hash. If you deployed to one host, only the API key would be needed (as in development and test). + +development: + ABQIAAAAzMUFFnT9uH0xq39J0Y4kbhTJQa0g3IQ9GZqIMmInSLzwtGDKaBR6j135zrztfTGVOm2QlWnkaidDIQ + +test: + ABQIAAAAzMUFFnT9uH0xq39J0Y4kbhTJQa0g3IQ9GZqIMmInSLzwtGDKaBR6j135zrztfTGVOm2QlWnkaidDIQ + +production: + thepochisuperstarmegashow.com: ABQIAAAAzMUFFnT9uH0Sfg76Y4kbhTJQa0g3IQ9GZqIMmInSLzwtGDmlRT6e90j135zat56yhJKQlWnkaidDIQ example.com: ABQIAAAAzMUFFnT9uH0Sfg98Y4kbhGFJQa0g3IQ9GZqIMmInSLrthJKGDmlRT98f4j135zat56yjRKQlWnkmod3TB \ No newline at end of file diff --git a/init.rb b/init.rb index 7316aef..5797149 100644 --- a/init.rb +++ b/init.rb @@ -1,3 +1,3 @@ -require 'ym4r_gm' - - +require 'ym4r_gm' + + diff --git a/javascript/markerGroup.js b/javascript/markerGroup.js index 02fe624..021c9db 100644 --- a/javascript/markerGroup.js +++ b/javascript/markerGroup.js @@ -1,114 +1,114 @@ -function GMarkerGroup(active, markers, markersById) { - this.active = active; - this.markers = markers || new Array(); - this.markersById = markersById || new Object(); -} - -GMarkerGroup.prototype = new GOverlay(); - -GMarkerGroup.prototype.initialize = function(map) { - this.map = map; - - if(this.active){ - for(var i = 0 , len = this.markers.length; i < len; i++) { - this.map.addOverlay(this.markers[i]); - } - for(var id in this.markersById){ - this.map.addOverlay(this.markersById[id]); - } - } -} - -//If not already done (ie if not inactive) remove all the markers from the map -GMarkerGroup.prototype.remove = function() { - this.deactivate(); -} - -GMarkerGroup.prototype.redraw = function(force){ - //Nothing to do : markers are already taken care of -} - -//Copy the data to a new Marker Group -GMarkerGroup.prototype.copy = function() { - var overlay = new GMarkerGroup(this.active); - overlay.markers = this.markers; //Need to do deep copy - overlay.markersById = this.markersById; //Need to do deep copy - return overlay; -} - -//Inactivate the Marker group and clear the internal content -GMarkerGroup.prototype.clear = function(){ - //deactivate the map first (which removes the markers from the map) - this.deactivate(); - //Clear the internal content - this.markers = new Array(); - this.markersById = new Object(); -} - -//Add a marker to the GMarkerGroup ; Adds it now to the map if the GMarkerGroup is active -GMarkerGroup.prototype.addMarker = function(marker,id){ - if(id == undefined){ - this.markers.push(marker); - }else{ - this.markersById[id] = marker; - } - if(this.active && this.map != undefined ){ - this.map.addOverlay(marker); - } -} - -//Open the info window (or info window tabs) of a marker -GMarkerGroup.prototype.showMarker = function(id){ - var marker = this.markersById[id]; - if(marker != undefined){ - GEvent.trigger(marker,"click"); - } -} - -//Activate (or deactivate depending on the argument) the GMarkerGroup -GMarkerGroup.prototype.activate = function(active){ - active = (active == undefined) ? true : active; - if(!active){ - if(this.active){ - if(this.map != undefined){ - for(var i = 0 , len = this.markers.length; i < len; i++){ - this.map.removeOverlay(this.markers[i]) - } - for(var id in this.markersById){ - this.map.removeOverlay(this.markersById[id]); - } - } - this.active = false; - } - }else{ - if(!this.active){ - if(this.map != undefined){ - for(var i = 0 , len = this.markers.length; i < len; i++){ - this.map.addOverlay(this.markers[i]); - } - for(var id in this.markersById){ - this.map.addOverlay(this.markersById[id]); - } - } - this.active = true; - } - } -} - -GMarkerGroup.prototype.centerAndZoomOnMarkers = function() { - if(this.map != undefined){ - //merge markers and markersById - var tmpMarkers = this.markers.slice(); - for (var id in this.markersById){ - tmpMarkers.push(this.markersById[id]); - } - if(tmpMarkers.length > 0){ - this.map.centerAndZoomOnMarkers(tmpMarkers); - } - } -} - -//Deactivate the Group Overlay (convenience method) -GMarkerGroup.prototype.deactivate = function(){ - this.activate(false); -} +function GMarkerGroup(active, markers, markersById) { + this.active = active; + this.markers = markers || new Array(); + this.markersById = markersById || new Object(); +} + +GMarkerGroup.prototype = new GOverlay(); + +GMarkerGroup.prototype.initialize = function(map) { + this.map = map; + + if(this.active){ + for(var i = 0 , len = this.markers.length; i < len; i++) { + this.map.addOverlay(this.markers[i]); + } + for(var id in this.markersById){ + this.map.addOverlay(this.markersById[id]); + } + } +} + +//If not already done (ie if not inactive) remove all the markers from the map +GMarkerGroup.prototype.remove = function() { + this.deactivate(); +} + +GMarkerGroup.prototype.redraw = function(force){ + //Nothing to do : markers are already taken care of +} + +//Copy the data to a new Marker Group +GMarkerGroup.prototype.copy = function() { + var overlay = new GMarkerGroup(this.active); + overlay.markers = this.markers; //Need to do deep copy + overlay.markersById = this.markersById; //Need to do deep copy + return overlay; +} + +//Inactivate the Marker group and clear the internal content +GMarkerGroup.prototype.clear = function(){ + //deactivate the map first (which removes the markers from the map) + this.deactivate(); + //Clear the internal content + this.markers = new Array(); + this.markersById = new Object(); +} + +//Add a marker to the GMarkerGroup ; Adds it now to the map if the GMarkerGroup is active +GMarkerGroup.prototype.addMarker = function(marker,id){ + if(id == undefined){ + this.markers.push(marker); + }else{ + this.markersById[id] = marker; + } + if(this.active && this.map != undefined ){ + this.map.addOverlay(marker); + } +} + +//Open the info window (or info window tabs) of a marker +GMarkerGroup.prototype.showMarker = function(id){ + var marker = this.markersById[id]; + if(marker != undefined){ + GEvent.trigger(marker,"click"); + } +} + +//Activate (or deactivate depending on the argument) the GMarkerGroup +GMarkerGroup.prototype.activate = function(active){ + active = (active == undefined) ? true : active; + if(!active){ + if(this.active){ + if(this.map != undefined){ + for(var i = 0 , len = this.markers.length; i < len; i++){ + this.map.removeOverlay(this.markers[i]) + } + for(var id in this.markersById){ + this.map.removeOverlay(this.markersById[id]); + } + } + this.active = false; + } + }else{ + if(!this.active){ + if(this.map != undefined){ + for(var i = 0 , len = this.markers.length; i < len; i++){ + this.map.addOverlay(this.markers[i]); + } + for(var id in this.markersById){ + this.map.addOverlay(this.markersById[id]); + } + } + this.active = true; + } + } +} + +GMarkerGroup.prototype.centerAndZoomOnMarkers = function() { + if(this.map != undefined){ + //merge markers and markersById + var tmpMarkers = this.markers.slice(); + for (var id in this.markersById){ + tmpMarkers.push(this.markersById[id]); + } + if(tmpMarkers.length > 0){ + this.map.centerAndZoomOnMarkers(tmpMarkers); + } + } +} + +//Deactivate the Group Overlay (convenience method) +GMarkerGroup.prototype.deactivate = function(){ + this.activate(false); +} diff --git a/javascript/ym4r-gm.js b/javascript/ym4r-gm.js index 1c768df..0b433b5 100644 --- a/javascript/ym4r-gm.js +++ b/javascript/ym4r-gm.js @@ -1,117 +1,117 @@ -// JS helper functions for YM4R - -function addInfoWindowToMarker(marker,info,options){ - GEvent.addListener(marker, "click", function() {marker.openInfoWindowHtml(info,options);}); - return marker; -} - -function addInfoWindowTabsToMarker(marker,info,options){ - GEvent.addListener(marker, "click", function() {marker.openInfoWindowTabsHtml(info,options);}); - return marker; -} - -function addPropertiesToLayer(layer,getTile,copyright,opacity,isPng){ - layer.getTileUrl = getTile; - layer.getCopyright = copyright; - layer.getOpacity = opacity; - layer.isPng = isPng; - return layer; -} - -function addOptionsToIcon(icon,options){ - for(var k in options){ - icon[k] = options[k]; - } - return icon; -} - -function addCodeToFunction(func,code){ - if(func == undefined) - return code; - else{ - return function(){ - func(); - code(); - } - } -} - -function addGeocodingToMarker(marker,address){ - marker.orig_initialize = marker.initialize; - orig_redraw = marker.redraw; - marker.redraw = function(force){}; //empty the redraw method so no error when called by addOverlay. - marker.initialize = function(map){ - new GClientGeocoder().getLatLng(address, - function(latlng){ - if(latlng){ - marker.redraw = orig_redraw; - marker.orig_initialize(map); //init before setting point - marker.setPoint(latlng); - }//do nothing - }); - }; - return marker; -} - - - -GMap2.prototype.centerAndZoomOnMarkers = function(markers) { - var bounds = new GLatLngBounds(markers[0].getPoint(), - markers[0].getPoint()); - for (var i=1, len = markers.length ; i:key) or a host, (:host). - def self.get(request,options = {}) - api_key = ApiKey.get(options) - sensor = options[:sensor] || false - output = options[:output] || "kml" - output_encoding = options[:output_encoding] || "utf-8" - url = "http://maps.google.com/maps/geo?q=#{URI.encode(request)}&key=#{api_key}&sensor=#{sensor}&output=#{output}&oe=#{output_encoding}" - - res = open(url).read - - case output.to_sym - when :json - res = eval(res.gsub(":","=>")) #!!!EVAL EVAL EVAL EVAL!!! hopefully we can trust google... - placemarks = Placemarks.new(res['name'],res['Status']['code']) - if res['Placemark'] - placemark = res['Placemark'] - - placemark.each do |data| - - data_country = data['Country']['CountryNameCode'] rescue "" - data_administrative = data['Country']['AdministrativeArea']['AdministrativeAreaName'] rescue "" - data_sub_administrative = data['Country']['AdministrativeArea']['SubAdministrativeArea']['SubAdministrativeAreaName'] rescue "" - data_locality = data['Country']['AdministrativeArea']['SubAdministrativeArea']['Locality']['LocalityName'] rescue "" - data_dependent_locality = data['Country']['AdministrativeArea']['SubAdministrativeArea']['Locality']['DependentLocality']['DependentLocalityName'] rescue "" - data_thoroughfare = data['Country']['AdministrativeArea']['SubAdministrativeArea']['Locality']['DependentLocality']['Thoroughfare']['ThoroughfareName'] rescue "" - data_postal_code = data['Country']['AdministrativeArea']['SubAdministrativeArea']['Locality']['DependentLocality']['Thoroughfare']['PostalCode']['PostalCodeNumber'] rescue "" - lon, lat = data['Point']['coordinates'][0,2] - data_accuracy = data['Accuracy'] - unless data_accuracy.nil? - data_accuracy = data_accuracy.to_i - end - - placemarks << Geocoding::Placemark.new(data['address'], - data_country, - data_administrative, - data_sub_administrative, - data_locality, - data_dependent_locality, - data_thoroughfare, - data_postal_code, - lon, lat, data_accuracy) - - end - end - when :kml, :xml - - doc = REXML::Document.new(res) - - response = doc.elements['//Response'] - placemarks = Placemarks.new(response.elements['name'].text,response.elements['Status/code'].text.to_i) - response.elements.each(".//Placemark") do |placemark| - data = placemark.elements - data_country = data['.//CountryNameCode'] - data_administrative = data['.//AdministrativeAreaName'] - data_sub_administrative = data['.//SubAdministrativeAreaName'] - data_locality = data['.//LocalityName'] - data_dependent_locality = data['.//DependentLocalityName'] - data_thoroughfare = data['.//ThoroughfareName'] - data_postal_code = data['.//PostalCodeNumber'] - lon, lat = data['.//coordinates'].text.split(",")[0..1].collect {|l| l.to_f } - data_accuracy = data['.//*[local-name()="AddressDetails"]'].attributes['Accuracy'] - unless data_accuracy.nil? - data_accuracy = data_accuracy.to_i - end - placemarks << Geocoding::Placemark.new(data['address'].text, - data_country.nil? ? "" : data_country.text, - data_administrative.nil? ? "" : data_administrative.text, - data_sub_administrative.nil? ? "" : data_sub_administrative.text, - data_locality.nil? ? "" : data_locality.text, - data_dependent_locality.nil? ? "" : data_dependent_locality.text, - data_thoroughfare.nil? ? "" : data_thoroughfare.text, - data_postal_code.nil? ? "" : data_postal_code.text, - lon, lat, data_accuracy ) - end - end - - placemarks - end - - #Group of placemarks returned by the Geocoding service. If the result is valid the +status+ attribute should be equal to Geocoding::GE0_SUCCESS - class Placemarks < Array - attr_accessor :name,:status - - def initialize(name,status) - super(0) - @name = name - @status = status - end - end - - #A result from the Geocoding service. - class Placemark < Struct.new(:address,:country_code,:administrative_area,:sub_administrative_area,:locality,:dependent_locality,:thoroughfare,:postal_code,:longitude,:latitude,:accuracy) - def lonlat - [longitude,latitude] - end - - def latlon - [latitude,longitude] - end - end - end - end -end +require 'open-uri' +require 'rexml/document' + +module Ym4r + module GmPlugin + module Geocoding + + GEO_SUCCESS = 200 + GEO_MISSING_ADDRESS = 601 + GEO_UNKNOWN_ADDRESS = 602 + GEO_UNAVAILABLE_ADDRESS = 603 + GEO_BAD_KEY = 610 + GEO_TOO_MANY_QUERIES = 620 + GEO_SERVER_ERROR = 500 + + #Gets placemarks by querying the Google Maps Geocoding service with the +request+ string. Options can either an explicity GMaps API key (:key) or a host, (:host). + def self.get(request,options = {}) + api_key = ApiKey.get(options) + sensor = options[:sensor] || false + output = options[:output] || "kml" + output_encoding = options[:output_encoding] || "utf-8" + url = "http://maps.google.com/maps/geo?q=#{URI.encode(request)}&key=#{api_key}&sensor=#{sensor}&output=#{output}&oe=#{output_encoding}" + + res = open(url).read + + case output.to_sym + when :json + res = eval(res.gsub(":","=>")) #!!!EVAL EVAL EVAL EVAL!!! hopefully we can trust google... + placemarks = Placemarks.new(res['name'],res['Status']['code']) + if res['Placemark'] + placemark = res['Placemark'] + + placemark.each do |data| + + data_country = data['Country']['CountryNameCode'] rescue "" + data_administrative = data['Country']['AdministrativeArea']['AdministrativeAreaName'] rescue "" + data_sub_administrative = data['Country']['AdministrativeArea']['SubAdministrativeArea']['SubAdministrativeAreaName'] rescue "" + data_locality = data['Country']['AdministrativeArea']['SubAdministrativeArea']['Locality']['LocalityName'] rescue "" + data_dependent_locality = data['Country']['AdministrativeArea']['SubAdministrativeArea']['Locality']['DependentLocality']['DependentLocalityName'] rescue "" + data_thoroughfare = data['Country']['AdministrativeArea']['SubAdministrativeArea']['Locality']['DependentLocality']['Thoroughfare']['ThoroughfareName'] rescue "" + data_postal_code = data['Country']['AdministrativeArea']['SubAdministrativeArea']['Locality']['DependentLocality']['Thoroughfare']['PostalCode']['PostalCodeNumber'] rescue "" + lon, lat = data['Point']['coordinates'][0,2] + data_accuracy = data['Accuracy'] + unless data_accuracy.nil? + data_accuracy = data_accuracy.to_i + end + + placemarks << Geocoding::Placemark.new(data['address'], + data_country, + data_administrative, + data_sub_administrative, + data_locality, + data_dependent_locality, + data_thoroughfare, + data_postal_code, + lon, lat, data_accuracy) + + end + end + when :kml, :xml + + doc = REXML::Document.new(res) + + response = doc.elements['//Response'] + placemarks = Placemarks.new(response.elements['name'].text,response.elements['Status/code'].text.to_i) + response.elements.each(".//Placemark") do |placemark| + data = placemark.elements + data_country = data['.//CountryNameCode'] + data_administrative = data['.//AdministrativeAreaName'] + data_sub_administrative = data['.//SubAdministrativeAreaName'] + data_locality = data['.//LocalityName'] + data_dependent_locality = data['.//DependentLocalityName'] + data_thoroughfare = data['.//ThoroughfareName'] + data_postal_code = data['.//PostalCodeNumber'] + lon, lat = data['.//coordinates'].text.split(",")[0..1].collect {|l| l.to_f } + data_accuracy = data['.//*[local-name()="AddressDetails"]'].attributes['Accuracy'] + unless data_accuracy.nil? + data_accuracy = data_accuracy.to_i + end + placemarks << Geocoding::Placemark.new(data['address'].text, + data_country.nil? ? "" : data_country.text, + data_administrative.nil? ? "" : data_administrative.text, + data_sub_administrative.nil? ? "" : data_sub_administrative.text, + data_locality.nil? ? "" : data_locality.text, + data_dependent_locality.nil? ? "" : data_dependent_locality.text, + data_thoroughfare.nil? ? "" : data_thoroughfare.text, + data_postal_code.nil? ? "" : data_postal_code.text, + lon, lat, data_accuracy ) + end + end + + placemarks + end + + #Group of placemarks returned by the Geocoding service. If the result is valid the +status+ attribute should be equal to Geocoding::GE0_SUCCESS + class Placemarks < Array + attr_accessor :name,:status + + def initialize(name,status) + super(0) + @name = name + @status = status + end + end + + #A result from the Geocoding service. + class Placemark < Struct.new(:address,:country_code,:administrative_area,:sub_administrative_area,:locality,:dependent_locality,:thoroughfare,:postal_code,:longitude,:latitude,:accuracy) + def lonlat + [longitude,latitude] + end + + def latlon + [latitude,longitude] + end + end + end + end +end diff --git a/lib/gm_plugin/helper.rb b/lib/gm_plugin/helper.rb index 7c06b77..afc215b 100644 --- a/lib/gm_plugin/helper.rb +++ b/lib/gm_plugin/helper.rb @@ -1,72 +1,72 @@ - -Ym4r::GmPlugin::GPolyline.class_eval do - #Creates a GPolyline object from a georuby line string. Assumes the points of the line strings are stored in Longitude(x)/Latitude(y) order. - def self.from_georuby(line_string,color = nil,weight = nil,opacity = nil) - GPolyline.new(line_string.points.collect { |point| GLatLng.new([point.y,point.x])},color,weight,opacity) - end -end - -Ym4r::GmPlugin::GMarker.class_eval do - #Creates a GMarker object from a georuby point. Accepts the same options as the GMarker constructor. Assumes the points of the line strings are stored in Longitude(x)/Latitude(y) order. - def self.from_georuby(point,options = {}) - GMarker.new([point.y,point.x],options) - end -end - -Ym4r::GmPlugin::GLatLng.class_eval do - #Creates a GLatLng object from a georuby point. Assumes the points of the line strings are stored in Longitude(x)/Latitude(y) order. - def self.from_georuby(point,unbounded = nil) - GLatLng.new([point.y,point.x],unbounded) - end -end - -Ym4r::GmPlugin::GLatLngBounds.class_eval do - #Creates a GLatLng object from a georuby point. Assumes the points of the line strings are stored in Longitude(x)/Latitude(y) order. - def self.from_georuby(envelope) - GLatLngBounds.new(GLatLng.from_georuby(envelope.lower_corner), - GLatLng.from_georuby(envelope.upper_corner)) - end -end - -Ym4r::GmPlugin::GPolygon.class_eval do - #Creates a GPolygon object from a georuby polygon or line string. Assumes the points of the line strings are stored in Longitude(x)/Latitude(y) order. - def self.from_georuby(ls_or_p, stroke_color="#000000",stroke_weight=1,stroke_opacity=1.0,color="#ff0000",opacity=1.0) - if ls_or_p.is_a?(GeoRuby::SimpleFeatures::LineString) - GPolygon.new(ls_or_p.collect { |point| GLatLng.new([point.y,point.x])},stroke_color,stroke_weight,stroke_opacity,color,opacity) - else - GPolygon.new(ls_or_p[0].collect { |point| GLatLng.new([point.y,point.x])},stroke_color,stroke_weight,stroke_opacity,color,opacity) - end - end -end - -Ym4r::GmPlugin::GPolylineEncoded.class_eval do - def self.from_georuby(line_string, color = nil, weight = nil, opacity = nil) - encoded_points = GMapPolylineEncoder.new.encode( - line_string.points.collect {|p| [p.y, p.x]}) - GPolylineEncoded.new( - :points => encoded_points[:points], - :levels => encoded_points[:levels], - :num_levels => encoded_points[:numLevels], - :zoom_factor => encoded_points[:zoomFactor], - :color => color, - :weight => weight, - :opacity => opacity - ) - end -end - -Ym4r::GmPlugin::GPolygonEncoded.class_eval do - def self.from_georuby(ls_or_p, stroke_color="#000000",stroke_weight=1,stroke_opacity=1.0,color="#ff0000",opacity=1.0) - if ls_or_p.is_a?(GeoRuby::SimpleFeatures::LineString) - GPolygonEncoded.new( - GPolylineEncoded.from_georuby(ls_or_p, stroke_color, stroke_weight, stroke_opacity), - color.nil?, color, opacity, stroke_weight > 0) - else - polylines = ls_or_p.rings.collect do |line_string| - GPolylineEncoded.from_georuby(line_string, stroke_color, - stroke_weight, stroke_opacity) - end - GPolygonEncoded.new(polylines, true, color, opacity, true) - end - end -end + +Ym4r::GmPlugin::GPolyline.class_eval do + #Creates a GPolyline object from a georuby line string. Assumes the points of the line strings are stored in Longitude(x)/Latitude(y) order. + def self.from_georuby(line_string,color = nil,weight = nil,opacity = nil) + GPolyline.new(line_string.points.collect { |point| GLatLng.new([point.y,point.x])},color,weight,opacity) + end +end + +Ym4r::GmPlugin::GMarker.class_eval do + #Creates a GMarker object from a georuby point. Accepts the same options as the GMarker constructor. Assumes the points of the line strings are stored in Longitude(x)/Latitude(y) order. + def self.from_georuby(point,options = {}) + GMarker.new([point.y,point.x],options) + end +end + +Ym4r::GmPlugin::GLatLng.class_eval do + #Creates a GLatLng object from a georuby point. Assumes the points of the line strings are stored in Longitude(x)/Latitude(y) order. + def self.from_georuby(point,unbounded = nil) + GLatLng.new([point.y,point.x],unbounded) + end +end + +Ym4r::GmPlugin::GLatLngBounds.class_eval do + #Creates a GLatLng object from a georuby point. Assumes the points of the line strings are stored in Longitude(x)/Latitude(y) order. + def self.from_georuby(envelope) + GLatLngBounds.new(GLatLng.from_georuby(envelope.lower_corner), + GLatLng.from_georuby(envelope.upper_corner)) + end +end + +Ym4r::GmPlugin::GPolygon.class_eval do + #Creates a GPolygon object from a georuby polygon or line string. Assumes the points of the line strings are stored in Longitude(x)/Latitude(y) order. + def self.from_georuby(ls_or_p, stroke_color="#000000",stroke_weight=1,stroke_opacity=1.0,color="#ff0000",opacity=1.0) + if ls_or_p.is_a?(GeoRuby::SimpleFeatures::LineString) + GPolygon.new(ls_or_p.collect { |point| GLatLng.new([point.y,point.x])},stroke_color,stroke_weight,stroke_opacity,color,opacity) + else + GPolygon.new(ls_or_p[0].collect { |point| GLatLng.new([point.y,point.x])},stroke_color,stroke_weight,stroke_opacity,color,opacity) + end + end +end + +Ym4r::GmPlugin::GPolylineEncoded.class_eval do + def self.from_georuby(line_string, color = nil, weight = nil, opacity = nil) + encoded_points = GMapPolylineEncoder.new.encode( + line_string.points.collect {|p| [p.y, p.x]}) + GPolylineEncoded.new( + :points => encoded_points[:points], + :levels => encoded_points[:levels], + :num_levels => encoded_points[:numLevels], + :zoom_factor => encoded_points[:zoomFactor], + :color => color, + :weight => weight, + :opacity => opacity + ) + end +end + +Ym4r::GmPlugin::GPolygonEncoded.class_eval do + def self.from_georuby(ls_or_p, stroke_color="#000000",stroke_weight=1,stroke_opacity=1.0,color="#ff0000",opacity=1.0) + if ls_or_p.is_a?(GeoRuby::SimpleFeatures::LineString) + GPolygonEncoded.new( + GPolylineEncoded.from_georuby(ls_or_p, stroke_color, stroke_weight, stroke_opacity), + color.nil?, color, opacity, stroke_weight > 0) + else + polylines = ls_or_p.rings.collect do |line_string| + GPolylineEncoded.from_georuby(line_string, stroke_color, + stroke_weight, stroke_opacity) + end + GPolygonEncoded.new(polylines, true, color, opacity, true) + end + end +end diff --git a/lib/gm_plugin/key.rb b/lib/gm_plugin/key.rb index 0de9c18..e4de97b 100644 --- a/lib/gm_plugin/key.rb +++ b/lib/gm_plugin/key.rb @@ -1,37 +1,37 @@ -module Ym4r - module GmPlugin - class GMapsAPIKeyConfigFileNotFoundException < StandardError - end - - class AmbiguousGMapsAPIKeyException < StandardError - end - - #Class fo the manipulation of the API key - class ApiKey - #Read the API key config for the current ENV - unless File.exist?(RAILS_ROOT + '/config/gmaps_api_key.yml') - raise GMapsAPIKeyConfigFileNotFoundException.new("File RAILS_ROOT/config/gmaps_api_key.yml not found") - else - env = ENV['RAILS_ENV'] || RAILS_ENV - GMAPS_API_KEY = YAML.load_file(RAILS_ROOT + '/config/gmaps_api_key.yml')[env] - end - - def self.get(options = {}) - if options.has_key?(:key) - options[:key] - elsif GMAPS_API_KEY.is_a?(Hash) - #For this environment, multiple hosts are possible. - #:host must have been passed as option - if options.has_key?(:host) - GMAPS_API_KEY[options[:host]] - else - raise AmbiguousGMapsAPIKeyException.new(GMAPS_API_KEY.keys.join(",")) - end - else - #Only one possible key: take it and ignore the :host option if it is there - GMAPS_API_KEY - end - end - end - end -end +module Ym4r + module GmPlugin + class GMapsAPIKeyConfigFileNotFoundException < StandardError + end + + class AmbiguousGMapsAPIKeyException < StandardError + end + + #Class fo the manipulation of the API key + class ApiKey + #Read the API key config for the current ENV + unless File.exist?(RAILS_ROOT + '/config/gmaps_api_key.yml') + raise GMapsAPIKeyConfigFileNotFoundException.new("File RAILS_ROOT/config/gmaps_api_key.yml not found") + else + env = ENV['RAILS_ENV'] || RAILS_ENV + GMAPS_API_KEY = YAML.load_file(RAILS_ROOT + '/config/gmaps_api_key.yml')[env] + end + + def self.get(options = {}) + if options.has_key?(:key) + options[:key] + elsif GMAPS_API_KEY.is_a?(Hash) + #For this environment, multiple hosts are possible. + #:host must have been passed as option + if options.has_key?(:host) + GMAPS_API_KEY[options[:host]] + else + raise AmbiguousGMapsAPIKeyException.new(GMAPS_API_KEY.keys.join(",")) + end + else + #Only one possible key: take it and ignore the :host option if it is there + GMAPS_API_KEY + end + end + end + end +end diff --git a/lib/gm_plugin/layer.rb b/lib/gm_plugin/layer.rb index d7fc151..0720e47 100644 --- a/lib/gm_plugin/layer.rb +++ b/lib/gm_plugin/layer.rb @@ -1,126 +1,126 @@ -module Ym4r - module GmPlugin - #Map types of the map - class GMapType - include MappingObject - - G_NORMAL_MAP = Variable.new("G_NORMAL_MAP") - G_SATELLITE_MAP = Variable.new("G_SATELLITE_MAP") - G_HYBRID_MAP = Variable.new("G_HYBRID_MAP") - G_PHYSICAL_MAP = Variable.new("G_PHYSICAL_MAP") - - attr_accessor :layers, :name, :projection, :options - - #The options can be any of the GMapType options detailed in the documentation + a :projection. - def initialize(layers, name, options = {}) - @layers = layers - @name = name - @projection = options.delete(:projection) || GMercatorProjection.new - @options = options - end - - def create - "new GMapType(#{MappingObject.javascriptify_variable(Array(layers))}, #{MappingObject.javascriptify_variable(projection)}, #{MappingObject.javascriptify_variable(name)}, #{MappingObject.javascriptify_variable(options)})" - end - end - - #Represents a mercator projection for zoom levels 0 to 17 (more than that by passing an argument to the constructor) - class GMercatorProjection - include MappingObject - - attr_accessor :n - - def initialize(n = nil) - @n = n - end - - def create - if n.nil? - return "G_NORMAL_MAP.getProjection()" - else - "new GMercatorProjection(#{@n})" - end - end - end - - #Abstract Tile layer. Subclasses must implement a get_tile_url method. - class GTileLayer - include MappingObject - - attr_accessor :opacity, :zoom_range, :copyright, :format - - #Options are the following, with default values: - #:zoom_range (0..17), :copyright ({'prefix' => '', 'copyright_texts' => [""]}), :opacity (1.0), :format ("png") - def initialize(options = {}) - @opacity = options[:opacity] || 1.0 - @zoom_range = options[:zoom_range] || (0..17) - @copyright = options[:copyright] || {'prefix' => '', 'copyright_texts' => [""]} - @format = (options[:format] || "png").to_s - end - - def create - "addPropertiesToLayer(new GTileLayer(new GCopyrightCollection(\"\"),#{zoom_range.begin},#{zoom_range.end}),#{get_tile_url},function(a,b) {return #{MappingObject.javascriptify_variable(@copyright)};}\n,function() {return #{@opacity};},function(){return #{@format == "png"};})" - end - - #for subclasses to implement - def get_tile_url - end - end - - #Represents a pre tiled layer, taking images directly from a server, without using a server script. - class PreTiledLayer < GTileLayer - attr_accessor :base_url - - #Possible options are the same as for the GTileLayer constructor - def initialize(base_url,options = {}) - super(options) - @base_url = base_url - end - - #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} - def get_tile_url - "function(a,b) { return '#{@base_url}/tile_' + b + '_' + a.x + '_' + a.y + '.#{format}';}" - end - end - - #Represents a pretiled layer (it actually does not really matter where the tiles come from). Calls an action on the server to get back the tiles. It passes the action arguments x, y (coordinates of the tile) and z (zoom level). It can be used, for example, to return default tiles when the requested tile is not present. - class PreTiledLayerFromAction < PreTiledLayer - def get_tile_url - "function(a,b) { return '#{base_url}?x=' + a.x + '&y=' + a.y + '&z=' + b ;}" - end - end - - #Represents a TileLayer where the tiles are generated dynamically from a WMS server (MapServer, GeoServer,...) - #You need to include the JavaScript file wms-gs.js for this to work - #see http://docs.codehaus.org/display/GEOSDOC/Google+Maps - class WMSLayer < GTileLayer - attr_accessor :base_url, :layers, :styles, :merc_proj, :use_geographic - - #Options are the same as with GTileLayer + :styles (""), :merc_proj (:mapserver), :use_geographic (false) - def initialize(base_url, layers, options = {}) - super(options) - @base_url = base_url.gsub(/\?$/,"") #standardize the url - @layers = layers - @styles = options[:styles] || "" - merc_proj = options[:merc_proj] || :mapserver - @merc_proj = if merc_proj == :mapserver - "54004" - elsif merc_proj == :geoserver - "41001" - else - merc_proj.to_s - end - @use_geographic = options.has_key?(:use_geographic)? options[:use_geographic] : false - puts format - end - - def get_tile_url - "getTileUrlForWMS" - end - - def create - "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)})" - end - end - end -end +module Ym4r + module GmPlugin + #Map types of the map + class GMapType + include MappingObject + + G_NORMAL_MAP = Variable.new("G_NORMAL_MAP") + G_SATELLITE_MAP = Variable.new("G_SATELLITE_MAP") + G_HYBRID_MAP = Variable.new("G_HYBRID_MAP") + G_PHYSICAL_MAP = Variable.new("G_PHYSICAL_MAP") + + attr_accessor :layers, :name, :projection, :options + + #The options can be any of the GMapType options detailed in the documentation + a :projection. + def initialize(layers, name, options = {}) + @layers = layers + @name = name + @projection = options.delete(:projection) || GMercatorProjection.new + @options = options + end + + def create + "new GMapType(#{MappingObject.javascriptify_variable(Array(layers))}, #{MappingObject.javascriptify_variable(projection)}, #{MappingObject.javascriptify_variable(name)}, #{MappingObject.javascriptify_variable(options)})" + end + end + + #Represents a mercator projection for zoom levels 0 to 17 (more than that by passing an argument to the constructor) + class GMercatorProjection + include MappingObject + + attr_accessor :n + + def initialize(n = nil) + @n = n + end + + def create + if n.nil? + return "G_NORMAL_MAP.getProjection()" + else + "new GMercatorProjection(#{@n})" + end + end + end + + #Abstract Tile layer. Subclasses must implement a get_tile_url method. + class GTileLayer + include MappingObject + + attr_accessor :opacity, :zoom_range, :copyright, :format + + #Options are the following, with default values: + #:zoom_range (0..17), :copyright ({'prefix' => '', 'copyright_texts' => [""]}), :opacity (1.0), :format ("png") + def initialize(options = {}) + @opacity = options[:opacity] || 1.0 + @zoom_range = options[:zoom_range] || (0..17) + @copyright = options[:copyright] || {'prefix' => '', 'copyright_texts' => [""]} + @format = (options[:format] || "png").to_s + end + + def create + "addPropertiesToLayer(new GTileLayer(new GCopyrightCollection(\"\"),#{zoom_range.begin},#{zoom_range.end}),#{get_tile_url},function(a,b) {return #{MappingObject.javascriptify_variable(@copyright)};}\n,function() {return #{@opacity};},function(){return #{@format == "png"};})" + end + + #for subclasses to implement + def get_tile_url + end + end + + #Represents a pre tiled layer, taking images directly from a server, without using a server script. + class PreTiledLayer < GTileLayer + attr_accessor :base_url + + #Possible options are the same as for the GTileLayer constructor + def initialize(base_url,options = {}) + super(options) + @base_url = base_url + end + + #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} + def get_tile_url + "function(a,b) { return '#{@base_url}/tile_' + b + '_' + a.x + '_' + a.y + '.#{format}';}" + end + end + + #Represents a pretiled layer (it actually does not really matter where the tiles come from). Calls an action on the server to get back the tiles. It passes the action arguments x, y (coordinates of the tile) and z (zoom level). It can be used, for example, to return default tiles when the requested tile is not present. + class PreTiledLayerFromAction < PreTiledLayer + def get_tile_url + "function(a,b) { return '#{base_url}?x=' + a.x + '&y=' + a.y + '&z=' + b ;}" + end + end + + #Represents a TileLayer where the tiles are generated dynamically from a WMS server (MapServer, GeoServer,...) + #You need to include the JavaScript file wms-gs.js for this to work + #see http://docs.codehaus.org/display/GEOSDOC/Google+Maps + class WMSLayer < GTileLayer + attr_accessor :base_url, :layers, :styles, :merc_proj, :use_geographic + + #Options are the same as with GTileLayer + :styles (""), :merc_proj (:mapserver), :use_geographic (false) + def initialize(base_url, layers, options = {}) + super(options) + @base_url = base_url.gsub(/\?$/,"") #standardize the url + @layers = layers + @styles = options[:styles] || "" + merc_proj = options[:merc_proj] || :mapserver + @merc_proj = if merc_proj == :mapserver + "54004" + elsif merc_proj == :geoserver + "41001" + else + merc_proj.to_s + end + @use_geographic = options.has_key?(:use_geographic)? options[:use_geographic] : false + puts format + end + + def get_tile_url + "getTileUrlForWMS" + end + + def create + "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)})" + end + end + end +end diff --git a/lib/gm_plugin/map.rb b/lib/gm_plugin/map.rb index f00cac4..69efb24 100644 --- a/lib/gm_plugin/map.rb +++ b/lib/gm_plugin/map.rb @@ -1,301 +1,301 @@ -module Ym4r - module GmPlugin - #Representing the Google Maps API class GMap2. - class GMap - include MappingObject - - #A constant containing the declaration of the VML namespace, necessary to display polylines under IE. - VML_NAMESPACE = "xmlns:v=\"urn:schemas-microsoft-com:vml\"" - - #The id of the DIV that will contain the map in the HTML page. - attr_reader :container - - #By default the map in the HTML page will be globally accessible with the name +map+. - def initialize(container, variable = "map") - @container = container - @variable = variable - @init = [] - @init_end = [] #for stuff that must be initialized at the end (controls) - @init_begin = [] #for stuff that must be initialized at the beginning (center + zoom) - @global_init = [] - end - - #Deprecated. Use the static version instead. - def header(with_vml = true) - GMap.header(:with_vml => with_vml) - end - - #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 :with_vml => false as option to the method. You can also pass a :host 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 @request.host. If you have defined only one API key for the current environment, the :host option is ignored. Finally you can override all the key settings in the configuration by passing a value to the :key key. You can pass a language for the map type buttons with the :hl 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). Finally, you can pass :local_search => true to get the header css and js information needed for the local search control. If you do want local search you must also add :local_search => true to the @map.control_init method. - def self.header(options = {}) - options[:with_vml] = true unless options.has_key?(:with_vml) - options[:hl] ||= '' - options[:local_search] = false unless options.has_key?(:local_search) - options[:sensor] = false unless options.has_key?(:sensor) - options[:version] ||= "2.x" - api_key = ApiKey.get(options) - a = "\n" - a << "\n" unless options[:without_js] - a << "\n" if options[:with_vml] - a << "" if options[:local_search] - a << "\n" if options[:local_search] - a << "" if options[:local_search] - a - end - - #Outputs the
which has been configured to contain the map. You can pass :width and :height 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 :class to set the classname of the div. - # To include initial content in the div, such as a loading message, you - # may pass a :content option specifying a string, or other - # object, such as a REXML fragment, that responds to #to_s. - def div(options = {}) - attributes = "id=\"#{@container}\" " - if options.has_key?(:height) && options.has_key?(:width) - width = options.delete(:width) - if width.is_a?(Integer) or width =~ /^[0-9]+$/ - width = width.to_s + "px" - end - height = options.delete(:height) - if height.is_a?(Integer) or height =~ /^[0-9]+$/ - height = height.to_s + "px" - end - attributes += "style=\"width:#{width};height:#{height}\" " - end - if options.has_key?(:class) - attributes += options.keys.map {|opt| "#{opt}=\"#{options[opt]}\"" }.join(" ") - end - "
#{options[:content].to_s}
" - end - - #Outputs a style declaration setting the dimensions of the DIV container of the map. This info can also be set manually in a CSS. - def header_width_height(width,height) - "" - end - - #Records arbitrary JavaScript code and outputs it during initialization inside the +load+ function. - def record_init(code) - @init << code - end - - #Initializes the controls: you can pass a hash with keys :small_map, :large_map, :small_zoom, :scale, :map_type, :overview_map and hash of options controlling its display (:hide and :size), :local_search, :local_search_options, and :show_on_focus - def control_init(controls = {}) - @init_end << add_control(GSmallMapControl.new) if controls[:small_map] - @init_end << add_control(GLargeMapControl.new) if controls[:large_map] - @init_end << add_control(GSmallZoomControl.new) if controls[:small_zoom] - @init_end << add_control(GScaleControl.new) if controls[:scale] - @init_end << add_control(GMapTypeControl.new) if controls[:map_type] - @init_end << add_control(GHierarchicalMapTypeControl.new) if controls[:hierarchical_map_type] - if controls[:overview_map] - if controls[:overview_map].is_a?(Hash) - hide = controls[:overview_map][:hide] - size = controls[:overview_map][:size] - end - overview_control = GOverviewMapControl.new(size) - @global_init << overview_control.declare("#{@variable}_ovm") if hide - @init_end << add_control(overview_control) - @init_end << "#{overview_control.variable}.hide(true);" if hide - end - @init_end << add_control(GLocalSearchControl.new(controls[:anchor], controls[:offset_width], controls[:offset_height], controls[:local_search_options])) if controls[:local_search] - if controls[:show_on_focus] # Should be last - @init_end << "#{@variable}.hideControls();" - event_init(self, :mouseover, "function(){#{@variable}.showControls();}") - event_init(self, :mouseout, "function(){#{@variable}.hideControls();}") - end - end - - #Initializes the interface configuration: double-click zoom, dragging, continuous zoom,... You can pass a hash with keys :dragging, :info_window, :double_click_zoom, :continuous_zoom and :scroll_wheel_zoom. The values should be true or false. Check the google maps API doc to know what the default values are. - def interface_init(interface = {}) - if !interface[:dragging].nil? - if interface[:dragging] - @init << enableDragging() - else - @init << disableDragging() - end - end - if !interface[:info_window].nil? - if interface[:info_window] - @init << enableInfoWindow() - else - @init << disableInfoWindow() - end - end - if !interface[:double_click_zoom].nil? - if interface[:double_click_zoom] - @init << enableDoubleClickZoom() - else - @init << disableDoubleClickZoom() - end - end - if !interface[:continuous_zoom].nil? - if interface[:continuous_zoom] - @init << enableContinuousZoom() - else - @init << disableContinuousZoom() - end - end - if !interface[:scroll_wheel_zoom].nil? - if interface[:scroll_wheel_zoom] - @init << enableScrollWheelZoom() - else - @init << disableScrollWheelZoom() - end - end - end - - #Initializes the initial center and zoom of the map. +center+ can be both a GLatLng object or a 2-float array. - def center_zoom_init(center, zoom) - if center.is_a?(GLatLng) - @init_begin << set_center(center,zoom) - else - @init_begin << set_center(GLatLng.new(center),zoom) - end - end - - #Center and zoom based on the coordinates passed as argument (either 2D arrays or GLatLng objects) - def center_zoom_on_points_init(*points) - if(points.length > 0) - if(points[0].is_a?(Array)) - points = points.collect { |point| GLatLng.new(point) } - end - @init_begin << center_and_zoom_on_points(points) - end - end - - #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). - def center_zoom_on_bounds_init(latlngbounds) - if(latlngbounds.is_a?(Array)) - if latlngbounds[0].is_a?(Array) - latlngbounds = GLatLngBounds.new(GLatLng.new(latlngbounds[0]),GLatLng.new(latlngbounds[1])) - elsif latlngbounds[0].is_a?(GLatLng) - latlngbounds = GLatLngBounds.new(*latlngbounds) - end - end - #else it is already a latlngbounds object - - @init_begin << center_and_zoom_on_bounds(latlngbounds) - end - - #Initializes the map by adding an overlay (marker or polyline). - def overlay_init(overlay) - @init << add_overlay(overlay) - end - - #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+). - def add_map_type_init(map_type, add = true) - unless add - @init << get_map_types.set_property(:length,0) - end - @init << add_map_type(map_type) - end - #for legacy purpose - alias :map_type_init :add_map_type_init - - #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 add_map_type_init). Use set_map_type_init(GMapType::G_SATELLITE_MAP) or set_map_type_init(GMapType::G_HYBRID_MAP) to initialize the map with repsecitvely the Satellite view and the hybrid view. - def set_map_type_init(map_type) - @init << set_map_type(map_type) - end - - #Locally declare a MappingObject with variable name "name" - def declare_init(variable, name) - @init << variable.declare(name) - end - - #Records arbitrary JavaScript code and outputs it during initialization outside the +load+ function (ie globally). - def record_global_init(code) - @global_init << code - end - - #Deprecated. Use icon_global_init instead. - def icon_init(icon , name) - icon_global_init(icon , name) - end - - #Initializes an icon and makes it globally accessible through the JavaScript variable of name +variable+. - def icon_global_init(icon , name, options = {}) - declare_global_init(icon,name,options) - end - - #Registers an event - def event_init(object,event,callback) - @init << "GEvent.addListener(#{object.to_javascript},\"#{MappingObject.javascriptify_method(event.to_s)}\",#{callback});" - end - - #Registers an event globally - def event_global_init(object,event,callback) - @global_init << "GEvent.addListener(#{object.to_javascript},\"#{MappingObject.javascriptify_method(event.to_s)}\",#{callback});" - end - - #Declares the overlay globally with name +name+ - def overlay_global_init(overlay,name, options = {}) - declare_global_init(overlay,name, options) - @init << add_overlay(overlay) - end - - #Globally declare a MappingObject with variable name "name". Option :local_construction 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) - def declare_global_init(variable,name, options = {}) - unless options[:local_construction] - @global_init << "var #{variable.assign_to(name)}" - else - @global_init << "var #{name};" - @init << variable.assign_to(name) - end - end - - #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 :full, 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. - def to_html(options = {}) - no_load = options[:no_load] - no_script_tag = options[:no_script_tag] - no_declare = options[:no_declare] - no_global = options[:no_global] - fullscreen = options[:full] - load_pr = options[:proto_load] #to prevent some problems when the onload event callback from Prototype is used - - html = "" - html << "" if !no_script_tag - - if fullscreen - #setting up the style in case of full screen - html << "" - end - - html - end - - #Outputs in JavaScript the creation of a GMap2 object - def create - "new GMap2(document.getElementById(\"#{@container}\"))" - end - end - end -end - +module Ym4r + module GmPlugin + #Representing the Google Maps API class GMap2. + class GMap + include MappingObject + + #A constant containing the declaration of the VML namespace, necessary to display polylines under IE. + VML_NAMESPACE = "xmlns:v=\"urn:schemas-microsoft-com:vml\"" + + #The id of the DIV that will contain the map in the HTML page. + attr_reader :container + + #By default the map in the HTML page will be globally accessible with the name +map+. + def initialize(container, variable = "map") + @container = container + @variable = variable + @init = [] + @init_end = [] #for stuff that must be initialized at the end (controls) + @init_begin = [] #for stuff that must be initialized at the beginning (center + zoom) + @global_init = [] + end + + #Deprecated. Use the static version instead. + def header(with_vml = true) + GMap.header(:with_vml => with_vml) + end + + #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 :with_vml => false as option to the method. You can also pass a :host 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 @request.host. If you have defined only one API key for the current environment, the :host option is ignored. Finally you can override all the key settings in the configuration by passing a value to the :key key. You can pass a language for the map type buttons with the :hl 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). Finally, you can pass :local_search => true to get the header css and js information needed for the local search control. If you do want local search you must also add :local_search => true to the @map.control_init method. + def self.header(options = {}) + options[:with_vml] = true unless options.has_key?(:with_vml) + options[:hl] ||= '' + options[:local_search] = false unless options.has_key?(:local_search) + options[:sensor] = false unless options.has_key?(:sensor) + options[:version] ||= "2.x" + api_key = ApiKey.get(options) + a = "\n" + a << "\n" unless options[:without_js] + a << "\n" if options[:with_vml] + a << "" if options[:local_search] + a << "\n" if options[:local_search] + a << "" if options[:local_search] + a + end + + #Outputs the
which has been configured to contain the map. You can pass :width and :height 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 :class to set the classname of the div. + # To include initial content in the div, such as a loading message, you + # may pass a :content option specifying a string, or other + # object, such as a REXML fragment, that responds to #to_s. + def div(options = {}) + attributes = "id=\"#{@container}\" " + if options.has_key?(:height) && options.has_key?(:width) + width = options.delete(:width) + if width.is_a?(Integer) or width =~ /^[0-9]+$/ + width = width.to_s + "px" + end + height = options.delete(:height) + if height.is_a?(Integer) or height =~ /^[0-9]+$/ + height = height.to_s + "px" + end + attributes += "style=\"width:#{width};height:#{height}\" " + end + if options.has_key?(:class) + attributes += options.keys.map {|opt| "#{opt}=\"#{options[opt]}\"" }.join(" ") + end + "
#{options[:content].to_s}
" + end + + #Outputs a style declaration setting the dimensions of the DIV container of the map. This info can also be set manually in a CSS. + def header_width_height(width,height) + "" + end + + #Records arbitrary JavaScript code and outputs it during initialization inside the +load+ function. + def record_init(code) + @init << code + end + + #Initializes the controls: you can pass a hash with keys :small_map, :large_map, :small_zoom, :scale, :map_type, :overview_map and hash of options controlling its display (:hide and :size), :local_search, :local_search_options, and :show_on_focus + def control_init(controls = {}) + @init_end << add_control(GSmallMapControl.new) if controls[:small_map] + @init_end << add_control(GLargeMapControl.new) if controls[:large_map] + @init_end << add_control(GSmallZoomControl.new) if controls[:small_zoom] + @init_end << add_control(GScaleControl.new) if controls[:scale] + @init_end << add_control(GMapTypeControl.new) if controls[:map_type] + @init_end << add_control(GHierarchicalMapTypeControl.new) if controls[:hierarchical_map_type] + if controls[:overview_map] + if controls[:overview_map].is_a?(Hash) + hide = controls[:overview_map][:hide] + size = controls[:overview_map][:size] + end + overview_control = GOverviewMapControl.new(size) + @global_init << overview_control.declare("#{@variable}_ovm") if hide + @init_end << add_control(overview_control) + @init_end << "#{overview_control.variable}.hide(true);" if hide + end + @init_end << add_control(GLocalSearchControl.new(controls[:anchor], controls[:offset_width], controls[:offset_height], controls[:local_search_options])) if controls[:local_search] + if controls[:show_on_focus] # Should be last + @init_end << "#{@variable}.hideControls();" + event_init(self, :mouseover, "function(){#{@variable}.showControls();}") + event_init(self, :mouseout, "function(){#{@variable}.hideControls();}") + end + end + + #Initializes the interface configuration: double-click zoom, dragging, continuous zoom,... You can pass a hash with keys :dragging, :info_window, :double_click_zoom, :continuous_zoom and :scroll_wheel_zoom. The values should be true or false. Check the google maps API doc to know what the default values are. + def interface_init(interface = {}) + if !interface[:dragging].nil? + if interface[:dragging] + @init << enableDragging() + else + @init << disableDragging() + end + end + if !interface[:info_window].nil? + if interface[:info_window] + @init << enableInfoWindow() + else + @init << disableInfoWindow() + end + end + if !interface[:double_click_zoom].nil? + if interface[:double_click_zoom] + @init << enableDoubleClickZoom() + else + @init << disableDoubleClickZoom() + end + end + if !interface[:continuous_zoom].nil? + if interface[:continuous_zoom] + @init << enableContinuousZoom() + else + @init << disableContinuousZoom() + end + end + if !interface[:scroll_wheel_zoom].nil? + if interface[:scroll_wheel_zoom] + @init << enableScrollWheelZoom() + else + @init << disableScrollWheelZoom() + end + end + end + + #Initializes the initial center and zoom of the map. +center+ can be both a GLatLng object or a 2-float array. + def center_zoom_init(center, zoom) + if center.is_a?(GLatLng) + @init_begin << set_center(center,zoom) + else + @init_begin << set_center(GLatLng.new(center),zoom) + end + end + + #Center and zoom based on the coordinates passed as argument (either 2D arrays or GLatLng objects) + def center_zoom_on_points_init(*points) + if(points.length > 0) + if(points[0].is_a?(Array)) + points = points.collect { |point| GLatLng.new(point) } + end + @init_begin << center_and_zoom_on_points(points) + end + end + + #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). + def center_zoom_on_bounds_init(latlngbounds) + if(latlngbounds.is_a?(Array)) + if latlngbounds[0].is_a?(Array) + latlngbounds = GLatLngBounds.new(GLatLng.new(latlngbounds[0]),GLatLng.new(latlngbounds[1])) + elsif latlngbounds[0].is_a?(GLatLng) + latlngbounds = GLatLngBounds.new(*latlngbounds) + end + end + #else it is already a latlngbounds object + + @init_begin << center_and_zoom_on_bounds(latlngbounds) + end + + #Initializes the map by adding an overlay (marker or polyline). + def overlay_init(overlay) + @init << add_overlay(overlay) + end + + #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+). + def add_map_type_init(map_type, add = true) + unless add + @init << get_map_types.set_property(:length,0) + end + @init << add_map_type(map_type) + end + #for legacy purpose + alias :map_type_init :add_map_type_init + + #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 add_map_type_init). Use set_map_type_init(GMapType::G_SATELLITE_MAP) or set_map_type_init(GMapType::G_HYBRID_MAP) to initialize the map with repsecitvely the Satellite view and the hybrid view. + def set_map_type_init(map_type) + @init << set_map_type(map_type) + end + + #Locally declare a MappingObject with variable name "name" + def declare_init(variable, name) + @init << variable.declare(name) + end + + #Records arbitrary JavaScript code and outputs it during initialization outside the +load+ function (ie globally). + def record_global_init(code) + @global_init << code + end + + #Deprecated. Use icon_global_init instead. + def icon_init(icon , name) + icon_global_init(icon , name) + end + + #Initializes an icon and makes it globally accessible through the JavaScript variable of name +variable+. + def icon_global_init(icon , name, options = {}) + declare_global_init(icon,name,options) + end + + #Registers an event + def event_init(object,event,callback) + @init << "GEvent.addListener(#{object.to_javascript},\"#{MappingObject.javascriptify_method(event.to_s)}\",#{callback});" + end + + #Registers an event globally + def event_global_init(object,event,callback) + @global_init << "GEvent.addListener(#{object.to_javascript},\"#{MappingObject.javascriptify_method(event.to_s)}\",#{callback});" + end + + #Declares the overlay globally with name +name+ + def overlay_global_init(overlay,name, options = {}) + declare_global_init(overlay,name, options) + @init << add_overlay(overlay) + end + + #Globally declare a MappingObject with variable name "name". Option :local_construction 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) + def declare_global_init(variable,name, options = {}) + unless options[:local_construction] + @global_init << "var #{variable.assign_to(name)}" + else + @global_init << "var #{name};" + @init << variable.assign_to(name) + end + end + + #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 :full, 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. + def to_html(options = {}) + no_load = options[:no_load] + no_script_tag = options[:no_script_tag] + no_declare = options[:no_declare] + no_global = options[:no_global] + fullscreen = options[:full] + load_pr = options[:proto_load] #to prevent some problems when the onload event callback from Prototype is used + + html = "" + html << "" if !no_script_tag + + if fullscreen + #setting up the style in case of full screen + html << "" + end + + html + end + + #Outputs in JavaScript the creation of a GMap2 object + def create + "new GMap2(document.getElementById(\"#{@container}\"))" + end + end + end +end + diff --git a/lib/gm_plugin/mapping.rb b/lib/gm_plugin/mapping.rb index 66d825e..121ae80 100644 --- a/lib/gm_plugin/mapping.rb +++ b/lib/gm_plugin/mapping.rb @@ -1,128 +1,128 @@ -module Ym4r - module GmPlugin - #The module where all the Ruby-to-JavaScript conversion takes place. It is included by all the classes in the YM4R library. - module MappingObject - #The name of the variable in JavaScript space. - attr_reader :variable - - #Creates javascript code for missing methods + takes care of listeners - def method_missing(name,*args) - str_name = name.to_s - if str_name =~ /^on_(.*)/ - if args.length != 1 - raise ArgumentError("Only 1 argument is allowed on on_ methods"); - else - Variable.new("GEvent.addListener(#{to_javascript},\"#{MappingObject.javascriptify_method($1)}\",#{args[0]})") - end - else - args.collect! do |arg| - MappingObject.javascriptify_variable(arg) - end - Variable.new("#{to_javascript}.#{MappingObject.javascriptify_method(str_name)}(#{args.join(",")})") - end - end - - #Creates javascript code for array or hash indexing - def [](index) #index could be an integer or string - return Variable.new("#{to_javascript}[#{MappingObject.javascriptify_variable(index)}]") - end - - #Transforms a Ruby object into a JavaScript string : MAppingObject, String, Array, Hash and general case (using to_s) - def self.javascriptify_variable(arg) - if arg.is_a?(MappingObject) - arg.to_javascript - elsif arg.is_a?(String) - "\"#{MappingObject.escape_javascript(arg)}\"" - elsif arg.is_a?(Array) - "[" + arg.collect{ |a| MappingObject.javascriptify_variable(a)}.join(",") + "]" - elsif arg.is_a?(Hash) - "{" + arg.to_a.collect do |v| - "#{MappingObject.javascriptify_method(v[0].to_s)} : #{MappingObject.javascriptify_variable(v[1])}" - end.join(",") + "}" - elsif arg.nil? - "undefined" - else - arg.to_s - end - end - - #Escape string to be used in JavaScript. Lifted from rails. - def self.escape_javascript(javascript) - javascript.gsub(/\r\n|\n|\r/, "\\n").gsub("\"") { |m| "\\#{m}" } - end - - #Transform a ruby-type method name (like add_overlay) to a JavaScript-style one (like addOverlay). - def self.javascriptify_method(method_name) - method_name.gsub(/_(\w)/){|s| $1.upcase} - end - - #Declares a Mapping Object bound to a JavaScript variable of name +variable+. - def declare(variable) - @variable = variable - "var #{@variable} = #{create};" - end - - #declare with a random variable name - def declare_random(init,size = 8) - s = init.clone - 6.times { s << (i = Kernel.rand(62); i += ((i < 10) ? 48 : ((i < 36) ? 55 : 61 ))).chr } - declare(s) - end - - #Checks if the MappinObject has been declared - def declared? - !@variable.nil? - end - - #Binds a Mapping object to a previously declared JavaScript variable of name +variable+. - def assign_to(variable) - @variable = variable - "#{@variable} = #{create};" - end - - #Assign the +value+ to the +property+ of the MappingObject - def set_property(property, value) - "#{to_javascript}.#{MappingObject.javascriptify_method(property.to_s)} = #{MappingObject.javascriptify_variable(value)}" - end - - #Returns the code to get a +property+ from the MappingObject - def get_property(property) - Variable.new("#{to_javascript}.#{MappingObject.javascriptify_method(property.to_s)}") - end - - #Returns a Javascript code representing the object - def to_javascript - unless @variable.nil? - @variable - else - create - end - end - - #Creates a Mapping Object in JavaScript. - #To be implemented by subclasses if needed - def create - end - end - - #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. - class Variable - include MappingObject - - def initialize(variable) - @variable = variable - end - #Returns the javascript expression contained in the object. - def create - @variable - end - #Returns the expression inside the Variable followed by a ";" - def to_s - @variable + ";" - end - - UNDEFINED = Variable.new("undefined") - end - end -end - +module Ym4r + module GmPlugin + #The module where all the Ruby-to-JavaScript conversion takes place. It is included by all the classes in the YM4R library. + module MappingObject + #The name of the variable in JavaScript space. + attr_reader :variable + + #Creates javascript code for missing methods + takes care of listeners + def method_missing(name,*args) + str_name = name.to_s + if str_name =~ /^on_(.*)/ + if args.length != 1 + raise ArgumentError("Only 1 argument is allowed on on_ methods"); + else + Variable.new("GEvent.addListener(#{to_javascript},\"#{MappingObject.javascriptify_method($1)}\",#{args[0]})") + end + else + args.collect! do |arg| + MappingObject.javascriptify_variable(arg) + end + Variable.new("#{to_javascript}.#{MappingObject.javascriptify_method(str_name)}(#{args.join(",")})") + end + end + + #Creates javascript code for array or hash indexing + def [](index) #index could be an integer or string + return Variable.new("#{to_javascript}[#{MappingObject.javascriptify_variable(index)}]") + end + + #Transforms a Ruby object into a JavaScript string : MAppingObject, String, Array, Hash and general case (using to_s) + def self.javascriptify_variable(arg) + if arg.is_a?(MappingObject) + arg.to_javascript + elsif arg.is_a?(String) + "\"#{MappingObject.escape_javascript(arg)}\"" + elsif arg.is_a?(Array) + "[" + arg.collect{ |a| MappingObject.javascriptify_variable(a)}.join(",") + "]" + elsif arg.is_a?(Hash) + "{" + arg.to_a.collect do |v| + "#{MappingObject.javascriptify_method(v[0].to_s)} : #{MappingObject.javascriptify_variable(v[1])}" + end.join(",") + "}" + elsif arg.nil? + "undefined" + else + arg.to_s + end + end + + #Escape string to be used in JavaScript. Lifted from rails. + def self.escape_javascript(javascript) + javascript.gsub(/\r\n|\n|\r/, "\\n").gsub("\"") { |m| "\\#{m}" } + end + + #Transform a ruby-type method name (like add_overlay) to a JavaScript-style one (like addOverlay). + def self.javascriptify_method(method_name) + method_name.gsub(/_(\w)/){|s| $1.upcase} + end + + #Declares a Mapping Object bound to a JavaScript variable of name +variable+. + def declare(variable) + @variable = variable + "var #{@variable} = #{create};" + end + + #declare with a random variable name + def declare_random(init,size = 8) + s = init.clone + 6.times { s << (i = Kernel.rand(62); i += ((i < 10) ? 48 : ((i < 36) ? 55 : 61 ))).chr } + declare(s) + end + + #Checks if the MappinObject has been declared + def declared? + !@variable.nil? + end + + #Binds a Mapping object to a previously declared JavaScript variable of name +variable+. + def assign_to(variable) + @variable = variable + "#{@variable} = #{create};" + end + + #Assign the +value+ to the +property+ of the MappingObject + def set_property(property, value) + "#{to_javascript}.#{MappingObject.javascriptify_method(property.to_s)} = #{MappingObject.javascriptify_variable(value)}" + end + + #Returns the code to get a +property+ from the MappingObject + def get_property(property) + Variable.new("#{to_javascript}.#{MappingObject.javascriptify_method(property.to_s)}") + end + + #Returns a Javascript code representing the object + def to_javascript + unless @variable.nil? + @variable + else + create + end + end + + #Creates a Mapping Object in JavaScript. + #To be implemented by subclasses if needed + def create + end + end + + #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. + class Variable + include MappingObject + + def initialize(variable) + @variable = variable + end + #Returns the javascript expression contained in the object. + def create + @variable + end + #Returns the expression inside the Variable followed by a ";" + def to_s + @variable + ";" + end + + UNDEFINED = Variable.new("undefined") + end + end +end + diff --git a/lib/gm_plugin/overlay.rb b/lib/gm_plugin/overlay.rb index c05f074..d983228 100644 --- a/lib/gm_plugin/overlay.rb +++ b/lib/gm_plugin/overlay.rb @@ -1,400 +1,400 @@ -module Ym4r - module GmPlugin - #A graphical marker positionned through geographic coordinates (in the WGS84 datum). An HTML info window can be set to be displayed when the marker is clicked on. - class GMarker - include MappingObject - attr_accessor :point, :options, :info_window, :info_window_tabs, :address - #The +points+ argument can be either a GLatLng object or an array of 2 floats. The +options+ keys can be: :icon, :clickable, :title, :info_window and :info_window_tabs, as well as :max_width. The value of the +info_window+ key is a string of HTML code that will be displayed when the markers is clicked on. The value of the +info_window_tabs+ key is an array of GInfoWindowTab objects or a hash directly, in which case it will be transformed to an array of GInfoWindowTabs, with the keys as the tab headers and the values as the content. - def initialize(position, options = {}) - if position.is_a?(Array) - @point = GLatLng.new(position) - elsif position.is_a?(String) - @point = Variable.new("INVISIBLE") #default coordinates: won't appear anyway - @address = position - else - @point = position - end - @info_window = options.delete(:info_window) - @info_window_tabs = options.delete(:info_window_tabs) - if options.has_key?(:max_url) - @info_window_options = {:max_url => options.delete(:max_url) } - else - @info_window_options = {} - end - @options = options - end - #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. - def create - if @options.empty? - creation = "new GMarker(#{MappingObject.javascriptify_variable(@point)})" - else - creation = "new GMarker(#{MappingObject.javascriptify_variable(@point)},#{MappingObject.javascriptify_variable(@options)})" - end - if @info_window && @info_window.is_a?(String) - creation = "addInfoWindowToMarker(#{creation},#{MappingObject.javascriptify_variable(@info_window)},#{MappingObject.javascriptify_variable(@info_window_options)})" - elsif @info_window_tabs && @info_window_tabs.is_a?(Hash) - creation = "addInfoWindowTabsToMarker(#{creation},#{MappingObject.javascriptify_variable(@info_window_tabs.to_a.collect{|kv| GInfoWindowTab.new(kv[0],kv[1] ) })},#{MappingObject.javascriptify_variable(@info_window_options)})" - elsif @info_window_tabs - creation = "addInfoWindowTabsToMarker(#{creation},#{MappingObject.javascriptify_variable(Array(@info_window_tabs))},#{MappingObject.javascriptify_variable(@info_window_options)})" - end - if @address.nil? - creation - else - "addGeocodingToMarker(#{creation},#{MappingObject.javascriptify_variable(@address)})" - end - end - end - - #Represents a tab to be displayed in a bubble when a marker is clicked on. - class GInfoWindowTab < Struct.new(:tab,:content) - include MappingObject - def create - "new GInfoWindowTab(#{MappingObject.javascriptify_variable(tab)},#{MappingObject.javascriptify_variable(content)})" - end - end - - #Represents a definition of an icon. You can pass rubyfied versions of the attributes detailed in the Google Maps API documentation. You can initialize global icons to be used in the application by passing a icon object, along with a variable name, to GMap#icon_init. If you want to declare an icon outside this, you will need to declare it first, since the JavaScript constructor does not accept any argument. - class GIcon - include MappingObject - DEFAULT = Variable.new("G_DEFAULT_ICON") - attr_accessor :options, :copy_base - - #Options can contain all the attributes (in rubyfied format) of a GIcon object (see Google's doc), as well as :copy_base, which indicates if the icon is copied from another one. - def initialize(options = {}) - @copy_base = options.delete(:copy_base) - @options = options - end - #Creates a GIcon. - def create - if @copy_base - c = "new GIcon(#{MappingObject.javascriptify_variable(@copy_base)})" - else - c = "new GIcon()" - end - if !options.empty? - "addOptionsToIcon(#{c},#{MappingObject.javascriptify_variable(@options)})" - else - c - end - end - end - - #A polyline. - class GPolyline - include MappingObject - attr_accessor :points,:color,:weight,:opacity - #Can take an array of +GLatLng+ or an array of 2D arrays. A method to directly build a polyline from a GeoRuby linestring is provided in the helper.rb file. - def initialize(points,color = nil,weight = nil,opacity = nil) - if !points.empty? and points[0].is_a?(Array) - @points = points.collect { |pt| GLatLng.new(pt) } - else - @points = points - end - @color = color - @weight = weight - @opacity = opacity - end - #Creates a new polyline. - def create - a = "new GPolyline(#{MappingObject.javascriptify_variable(points)}" - a << ",#{MappingObject.javascriptify_variable(@color)}" if @color - a << ",#{MappingObject.javascriptify_variable(@weight)}" if @weight - a << ",#{MappingObject.javascriptify_variable(@opacity)}" if @opacity - a << ")" - end - end - - # - # Encoded GPolyline class. This class does NOT perform encoding. - # Instead, you must pass it encoded points, levels, and a zoom_factor - # created using Ym4r::GmPlugin::GMapPolylineEncoder. - # - # For more info on encoding polylines, see - # http://code.google.com/apis/maps/documentation/reference.html#GPolyline.fromEncoded - # http://code.google.com/apis/maps/documentation/overlays.html#Encoded_Polylines - # - class GPolylineEncoded - include MappingObject - attr_accessor :points,:color,:weight,:opacity,:levels,:zoom_factor,:num_levels - - def initialize(options={}) - @points = options[:points] - @color = options[:color] - @weight = options[:weight] - @opacity = options[:opacity] - @levels = options[:levels] || "BBBBBBBBBBBB" - @zoom_factor = options[:zoom_factor] || 32 - @num_levels = options[:num_levels] || 4 - end - def create - a = "new GPolyline.fromEncoded({points: #{MappingObject.javascriptify_variable(points)},\n" - a << "levels: #{MappingObject.javascriptify_variable(@levels)}," - a << "zoomFactor: #{MappingObject.javascriptify_variable(@zoom_factor)}," - a << "numLevels: #{MappingObject.javascriptify_variable(@num_levels)}" - a << ",color: #{MappingObject.javascriptify_variable(@color)}" if @color - a << ",weight: #{MappingObject.javascriptify_variable(@weight)}" if @weight - a << ",opacity: #{MappingObject.javascriptify_variable(@opacity)}" if @opacity - a << "})" - end - end - - #A basic Latitude/longitude point. - class GLatLng - include MappingObject - attr_accessor :lat,:lng,:unbounded - - def initialize(latlng,unbounded = nil) - @lat = latlng[0] - @lng = latlng[1] - @unbounded = unbounded - end - def create - unless @unbounded - "new GLatLng(#{MappingObject.javascriptify_variable(@lat)},#{MappingObject.javascriptify_variable(@lng)})" - else - "new GLatLng(#{MappingObject.javascriptify_variable(@lat)},#{MappingObject.javascriptify_variable(@lng)},#{MappingObject.javascriptify_variable(@unbounded)})" - end - end - end - - #A rectangular bounding box, defined by its south-western and north-eastern corners. - class GLatLngBounds < Struct.new(:sw,:ne) - include MappingObject - def create - "new GLatLngBounds(#{MappingObject.javascriptify_variable(sw)},#{MappingObject.javascriptify_variable(ne)})" - end - end - - # Wrapper for the Google Maps GPolygon class. See the Google Maps API - # docs: - # http://code.google.com/apis/maps/documentation/reference.html#GPolygon - class GPolygon - include MappingObject - - attr_accessor :points,:stroke_color,:stroke_weight,:stroke_opacity,:color,:opacity - - #Can take an array of +GLatLng+ or an array of 2D arrays. A method to directly build a polygon from a GeoRuby polygon is provided in the helper.rb file. - def initialize(points,stroke_color="#000000",stroke_weight=1,stroke_opacity=1.0,color="#ff0000",opacity=1.0,encoded=false) - if !points.empty? and points[0].is_a?(Array) - @points = points.collect { |pt| GLatLng.new(pt) } - else - @points = points - end - @stroke_color = stroke_color - @stroke_weight = stroke_weight - @stroke_opacity = stroke_opacity - @color = color - @opacity = opacity - end - - #Creates a new polygon - def create - a = "new GPolygon(#{MappingObject.javascriptify_variable(points)}" - a << ",#{MappingObject.javascriptify_variable(@stroke_color)}" - a << ",#{MappingObject.javascriptify_variable(@stroke_weight)}" - a << ",#{MappingObject.javascriptify_variable(@stroke_opacity)}" - a << ",#{MappingObject.javascriptify_variable(@color)}" - a << ",#{MappingObject.javascriptify_variable(@opacity)}" - a << ")" - end - end - - # - # Wrapper class for Google Maps GPolygons created using the - # GPolygon.fromEncoded() factory method. Unlike a regular GPolygon, it - # creates a polygon from a string of specially encoded points, allowing - # complex polygons with multiple rings to be rendered. When creating a - # GPolygonEncoded, pass it GPolylineEncoded objects. - # - class GPolygonEncoded - include MappingObject - - attr_accessor :polyline, :color, :opacity, :outline, :fill - - def initialize(polylines,fill=true,color="#000000",opacity=0.5,outline=false) - #force polylines to be an array - if polylines.is_a? Array - @polylines = polylines - else - @polylines = [polylines] - end - @color = color - @fill = fill - @opacity = opacity - @outline = outline - end - - #Creates a new polygon. - def create - polylines_for_polygon= [] - @polylines.each do |p| - x = "{points: #{MappingObject.javascriptify_variable(p.points)}," - x << "levels: #{MappingObject.javascriptify_variable(p.levels)}," - x << "zoomFactor: #{MappingObject.javascriptify_variable(p.zoom_factor)}," - x << "numLevels: #{MappingObject.javascriptify_variable(p.num_levels)}" - x << ", color: #{MappingObject.javascriptify_variable(p.color)}" if p.color - x << ", weight: #{MappingObject.javascriptify_variable(p.weight)}" if p.weight - x << ", opacity: #{MappingObject.javascriptify_variable(p.opacity)}" if p.opacity - x << "}" - polylines_for_polygon << x - end - - polylines_for_polygon = "[" + polylines_for_polygon.join(",") + "]" - - a = "new GPolygon.fromEncoded({polylines: #{polylines_for_polygon}," - a << "fill: #{MappingObject.javascriptify_variable(@fill)}," - a << "color: #{MappingObject.javascriptify_variable(@color)}," - a << "opacity: #{MappingObject.javascriptify_variable(@opacity)}," - a << "outline: #{MappingObject.javascriptify_variable(@outline)}" - a << "})" - end - end - - class ELabel - attr_accessor :point, :text, :style - include MappingObject - - def initialize(point, text=nil, style=nil) - @point = point - @text = text - @style = style - end - - def create - a = "new ELabel(#{MappingObject.javascriptify_variable(@point)}" - a << ",#{MappingObject.javascriptify_variable(@text)}" if @text - a << ",#{MappingObject.javascriptify_variable(@style)}" if @style - a << ")" - end - end - - - #A GGeoXml object gets data from a GeoRSS or KML feed and displays it. Use overlay_init to add it to a map at initialization time. - class GGeoXml - include MappingObject - - attr_accessor :url - - def initialize(url) - @url = url - end - - def create - "new GGeoXml(#{MappingObject.javascriptify_variable(@url)})" - end - - end - - #A GOverlay representing a group of GMarkers. The GMarkers can be identified with an id, which can be used to show the info window of a specific marker, in reponse, for example, to a click on a link. The whole group can be shown on and off at once. It should be declared global at initialization time to be useful. - class GMarkerGroup - include MappingObject - attr_accessor :active, :markers, :markers_by_id - - def initialize(active = true , markers = nil) - @active = active - @markers = [] - @markers_by_id = {} - if markers.is_a?(Array) - @markers = markers - elsif markers.is_a?(Hash) - @markers_by_id = markers - end - end - - def create - "new GMarkerGroup(#{MappingObject.javascriptify_variable(@active)},#{MappingObject.javascriptify_variable(@markers)},#{MappingObject.javascriptify_variable(@markers_by_id)})" - end - end - - #Can be used to implement a clusterer, similar to the clusterer below, except that there is more stuff to manage explicitly byt the programmer (but this is also more flexible). See the README for usage esamples. - class GMarkerManager - include MappingObject - - attr_accessor :map,:options,:managed_markers - - #options can be :border_padding, :max_zoom, :track_markers and :managed_markers: managed_markers must be an array of ManagedMarker objects - def initialize(map, options = {}) - @map = map - @managed_markers = Array(options.delete(:managed_markers)) #[] if nil - @options = options - end - - def create - puts @options.inspect - "addMarkersToManager(new GMarkerManager(#{MappingObject.javascriptify_variable(@map)},#{MappingObject.javascriptify_variable(@options)}),#{MappingObject.javascriptify_variable(@managed_markers)})" - end - - end - - #A set of similarly managed markers: They share the same minZoom and maxZoom. - class ManagedMarker - include MappingObject - - attr_accessor :markers,:min_zoom, :max_zoom - - def initialize(markers,min_zoom,max_zoom = nil) - @markers = markers - @min_zoom = min_zoom - @max_zoom = max_zoom - end - - def create - "new ManagedMarker(#{MappingObject.javascriptify_variable(@markers)},#{MappingObject.javascriptify_variable(@min_zoom)},#{MappingObject.javascriptify_variable(@max_zoom)})" - end - - end - - #Makes the link with the Clusterer2 library by Jef Poskanzer (slightly modified though). Is a GOverlay making clusters out of its GMarkers, so that GMarkers very close to each other appear as one when the zoom is low. When the zoom gets higher, the individual markers are drawn. - class Clusterer - include MappingObject - attr_accessor :markers,:icon, :max_visible_markers, :grid_size, :min_markers_per_cluster , :max_lines_per_info_box - - def initialize(markers = [], options = {}) - @markers = markers - @icon = options[:icon] || GIcon::DEFAULT - @max_visible_markers = options[:max_visible_markers] || 150 - @grid_size = options[:grid_size] || 5 - @min_markers_per_cluster = options[:min_markers_per_cluster] || 5 - @max_lines_per_info_box = options[:max_lines_per_info_box] || 10 - end - - def create - js_marker = '[' + @markers.collect do |marker| - add_description(marker) - end.join(",") + ']' - - "new Clusterer(#{js_marker},#{MappingObject.javascriptify_variable(@icon)},#{MappingObject.javascriptify_variable(@max_visible_markers)},#{MappingObject.javascriptify_variable(@grid_size)},#{MappingObject.javascriptify_variable(@min_markers_per_cluster)},#{MappingObject.javascriptify_variable(@max_lines_per_info_box)})" - end - - private - def add_description(marker) - "addDescriptionToMarker(#{MappingObject.javascriptify_variable(marker)},#{MappingObject.javascriptify_variable(marker.options[:description] || marker.options[:title] || '')})" - end - end - - #Makes the link with the MGeoRSS extension by Mikel Maron (a bit modified though). It lets you overlay on top of Google Maps the items present in a RSS feed that has GeoRss data. This data can be either in W3C Geo vocabulary or in the GeoRss Simple format. See http://georss.org to know more about GeoRss. - class GeoRssOverlay - include MappingObject - attr_accessor :url, :proxy, :icon, :options - - #You can pass the following options: - #- :icon: An icon for the items of the feed. Defaults to the classic red balloon icon. - #- :proxy: An URL on your server where fetching the RSS feed will be taken care of. - #- :list_div: In case you want a list of all the markers, with a link on which you can click in order to display the info on the marker, use this option to indicate the ID of the div (that you must place yourself). - #- :list_item_class: class of the DIV containing each item of the list. Ignored if option :list_div is not set. - #- :limit: Maximum number of items to display on the map. - #- :content_div: Instead of having an info window appear, indicates the ID of the DIV where this info should be displayed. - def initialize(url, options = {}) - @url = url - @icon = options.delete(:icon) || GIcon::DEFAULT - @proxy = options.delete(:proxy) || Variable::UNDEFINED - @options = options - end - - def create - "new GeoRssOverlay(#{MappingObject.javascriptify_variable(@url)},#{MappingObject.javascriptify_variable(@icon)},#{MappingObject.javascriptify_variable(@proxy)},#{MappingObject.javascriptify_variable(@options)})" - end - end - - end -end +module Ym4r + module GmPlugin + #A graphical marker positionned through geographic coordinates (in the WGS84 datum). An HTML info window can be set to be displayed when the marker is clicked on. + class GMarker + include MappingObject + attr_accessor :point, :options, :info_window, :info_window_tabs, :address + #The +points+ argument can be either a GLatLng object or an array of 2 floats. The +options+ keys can be: :icon, :clickable, :title, :info_window and :info_window_tabs, as well as :max_width. The value of the +info_window+ key is a string of HTML code that will be displayed when the markers is clicked on. The value of the +info_window_tabs+ key is an array of GInfoWindowTab objects or a hash directly, in which case it will be transformed to an array of GInfoWindowTabs, with the keys as the tab headers and the values as the content. + def initialize(position, options = {}) + if position.is_a?(Array) + @point = GLatLng.new(position) + elsif position.is_a?(String) + @point = Variable.new("INVISIBLE") #default coordinates: won't appear anyway + @address = position + else + @point = position + end + @info_window = options.delete(:info_window) + @info_window_tabs = options.delete(:info_window_tabs) + if options.has_key?(:max_url) + @info_window_options = {:max_url => options.delete(:max_url) } + else + @info_window_options = {} + end + @options = options + end + #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. + def create + if @options.empty? + creation = "new GMarker(#{MappingObject.javascriptify_variable(@point)})" + else + creation = "new GMarker(#{MappingObject.javascriptify_variable(@point)},#{MappingObject.javascriptify_variable(@options)})" + end + if @info_window && @info_window.is_a?(String) + creation = "addInfoWindowToMarker(#{creation},#{MappingObject.javascriptify_variable(@info_window)},#{MappingObject.javascriptify_variable(@info_window_options)})" + elsif @info_window_tabs && @info_window_tabs.is_a?(Hash) + creation = "addInfoWindowTabsToMarker(#{creation},#{MappingObject.javascriptify_variable(@info_window_tabs.to_a.collect{|kv| GInfoWindowTab.new(kv[0],kv[1] ) })},#{MappingObject.javascriptify_variable(@info_window_options)})" + elsif @info_window_tabs + creation = "addInfoWindowTabsToMarker(#{creation},#{MappingObject.javascriptify_variable(Array(@info_window_tabs))},#{MappingObject.javascriptify_variable(@info_window_options)})" + end + if @address.nil? + creation + else + "addGeocodingToMarker(#{creation},#{MappingObject.javascriptify_variable(@address)})" + end + end + end + + #Represents a tab to be displayed in a bubble when a marker is clicked on. + class GInfoWindowTab < Struct.new(:tab,:content) + include MappingObject + def create + "new GInfoWindowTab(#{MappingObject.javascriptify_variable(tab)},#{MappingObject.javascriptify_variable(content)})" + end + end + + #Represents a definition of an icon. You can pass rubyfied versions of the attributes detailed in the Google Maps API documentation. You can initialize global icons to be used in the application by passing a icon object, along with a variable name, to GMap#icon_init. If you want to declare an icon outside this, you will need to declare it first, since the JavaScript constructor does not accept any argument. + class GIcon + include MappingObject + DEFAULT = Variable.new("G_DEFAULT_ICON") + attr_accessor :options, :copy_base + + #Options can contain all the attributes (in rubyfied format) of a GIcon object (see Google's doc), as well as :copy_base, which indicates if the icon is copied from another one. + def initialize(options = {}) + @copy_base = options.delete(:copy_base) + @options = options + end + #Creates a GIcon. + def create + if @copy_base + c = "new GIcon(#{MappingObject.javascriptify_variable(@copy_base)})" + else + c = "new GIcon()" + end + if !options.empty? + "addOptionsToIcon(#{c},#{MappingObject.javascriptify_variable(@options)})" + else + c + end + end + end + + #A polyline. + class GPolyline + include MappingObject + attr_accessor :points,:color,:weight,:opacity + #Can take an array of +GLatLng+ or an array of 2D arrays. A method to directly build a polyline from a GeoRuby linestring is provided in the helper.rb file. + def initialize(points,color = nil,weight = nil,opacity = nil) + if !points.empty? and points[0].is_a?(Array) + @points = points.collect { |pt| GLatLng.new(pt) } + else + @points = points + end + @color = color + @weight = weight + @opacity = opacity + end + #Creates a new polyline. + def create + a = "new GPolyline(#{MappingObject.javascriptify_variable(points)}" + a << ",#{MappingObject.javascriptify_variable(@color)}" if @color + a << ",#{MappingObject.javascriptify_variable(@weight)}" if @weight + a << ",#{MappingObject.javascriptify_variable(@opacity)}" if @opacity + a << ")" + end + end + + # + # Encoded GPolyline class. This class does NOT perform encoding. + # Instead, you must pass it encoded points, levels, and a zoom_factor + # created using Ym4r::GmPlugin::GMapPolylineEncoder. + # + # For more info on encoding polylines, see + # http://code.google.com/apis/maps/documentation/reference.html#GPolyline.fromEncoded + # http://code.google.com/apis/maps/documentation/overlays.html#Encoded_Polylines + # + class GPolylineEncoded + include MappingObject + attr_accessor :points,:color,:weight,:opacity,:levels,:zoom_factor,:num_levels + + def initialize(options={}) + @points = options[:points] + @color = options[:color] + @weight = options[:weight] + @opacity = options[:opacity] + @levels = options[:levels] || "BBBBBBBBBBBB" + @zoom_factor = options[:zoom_factor] || 32 + @num_levels = options[:num_levels] || 4 + end + def create + a = "new GPolyline.fromEncoded({points: #{MappingObject.javascriptify_variable(points)},\n" + a << "levels: #{MappingObject.javascriptify_variable(@levels)}," + a << "zoomFactor: #{MappingObject.javascriptify_variable(@zoom_factor)}," + a << "numLevels: #{MappingObject.javascriptify_variable(@num_levels)}" + a << ",color: #{MappingObject.javascriptify_variable(@color)}" if @color + a << ",weight: #{MappingObject.javascriptify_variable(@weight)}" if @weight + a << ",opacity: #{MappingObject.javascriptify_variable(@opacity)}" if @opacity + a << "})" + end + end + + #A basic Latitude/longitude point. + class GLatLng + include MappingObject + attr_accessor :lat,:lng,:unbounded + + def initialize(latlng,unbounded = nil) + @lat = latlng[0] + @lng = latlng[1] + @unbounded = unbounded + end + def create + unless @unbounded + "new GLatLng(#{MappingObject.javascriptify_variable(@lat)},#{MappingObject.javascriptify_variable(@lng)})" + else + "new GLatLng(#{MappingObject.javascriptify_variable(@lat)},#{MappingObject.javascriptify_variable(@lng)},#{MappingObject.javascriptify_variable(@unbounded)})" + end + end + end + + #A rectangular bounding box, defined by its south-western and north-eastern corners. + class GLatLngBounds < Struct.new(:sw,:ne) + include MappingObject + def create + "new GLatLngBounds(#{MappingObject.javascriptify_variable(sw)},#{MappingObject.javascriptify_variable(ne)})" + end + end + + # Wrapper for the Google Maps GPolygon class. See the Google Maps API + # docs: + # http://code.google.com/apis/maps/documentation/reference.html#GPolygon + class GPolygon + include MappingObject + + attr_accessor :points,:stroke_color,:stroke_weight,:stroke_opacity,:color,:opacity + + #Can take an array of +GLatLng+ or an array of 2D arrays. A method to directly build a polygon from a GeoRuby polygon is provided in the helper.rb file. + def initialize(points,stroke_color="#000000",stroke_weight=1,stroke_opacity=1.0,color="#ff0000",opacity=1.0,encoded=false) + if !points.empty? and points[0].is_a?(Array) + @points = points.collect { |pt| GLatLng.new(pt) } + else + @points = points + end + @stroke_color = stroke_color + @stroke_weight = stroke_weight + @stroke_opacity = stroke_opacity + @color = color + @opacity = opacity + end + + #Creates a new polygon + def create + a = "new GPolygon(#{MappingObject.javascriptify_variable(points)}" + a << ",#{MappingObject.javascriptify_variable(@stroke_color)}" + a << ",#{MappingObject.javascriptify_variable(@stroke_weight)}" + a << ",#{MappingObject.javascriptify_variable(@stroke_opacity)}" + a << ",#{MappingObject.javascriptify_variable(@color)}" + a << ",#{MappingObject.javascriptify_variable(@opacity)}" + a << ")" + end + end + + # + # Wrapper class for Google Maps GPolygons created using the + # GPolygon.fromEncoded() factory method. Unlike a regular GPolygon, it + # creates a polygon from a string of specially encoded points, allowing + # complex polygons with multiple rings to be rendered. When creating a + # GPolygonEncoded, pass it GPolylineEncoded objects. + # + class GPolygonEncoded + include MappingObject + + attr_accessor :polyline, :color, :opacity, :outline, :fill + + def initialize(polylines,fill=true,color="#000000",opacity=0.5,outline=false) + #force polylines to be an array + if polylines.is_a? Array + @polylines = polylines + else + @polylines = [polylines] + end + @color = color + @fill = fill + @opacity = opacity + @outline = outline + end + + #Creates a new polygon. + def create + polylines_for_polygon= [] + @polylines.each do |p| + x = "{points: #{MappingObject.javascriptify_variable(p.points)}," + x << "levels: #{MappingObject.javascriptify_variable(p.levels)}," + x << "zoomFactor: #{MappingObject.javascriptify_variable(p.zoom_factor)}," + x << "numLevels: #{MappingObject.javascriptify_variable(p.num_levels)}" + x << ", color: #{MappingObject.javascriptify_variable(p.color)}" if p.color + x << ", weight: #{MappingObject.javascriptify_variable(p.weight)}" if p.weight + x << ", opacity: #{MappingObject.javascriptify_variable(p.opacity)}" if p.opacity + x << "}" + polylines_for_polygon << x + end + + polylines_for_polygon = "[" + polylines_for_polygon.join(",") + "]" + + a = "new GPolygon.fromEncoded({polylines: #{polylines_for_polygon}," + a << "fill: #{MappingObject.javascriptify_variable(@fill)}," + a << "color: #{MappingObject.javascriptify_variable(@color)}," + a << "opacity: #{MappingObject.javascriptify_variable(@opacity)}," + a << "outline: #{MappingObject.javascriptify_variable(@outline)}" + a << "})" + end + end + + class ELabel + attr_accessor :point, :text, :style + include MappingObject + + def initialize(point, text=nil, style=nil) + @point = point + @text = text + @style = style + end + + def create + a = "new ELabel(#{MappingObject.javascriptify_variable(@point)}" + a << ",#{MappingObject.javascriptify_variable(@text)}" if @text + a << ",#{MappingObject.javascriptify_variable(@style)}" if @style + a << ")" + end + end + + + #A GGeoXml object gets data from a GeoRSS or KML feed and displays it. Use overlay_init to add it to a map at initialization time. + class GGeoXml + include MappingObject + + attr_accessor :url + + def initialize(url) + @url = url + end + + def create + "new GGeoXml(#{MappingObject.javascriptify_variable(@url)})" + end + + end + + #A GOverlay representing a group of GMarkers. The GMarkers can be identified with an id, which can be used to show the info window of a specific marker, in reponse, for example, to a click on a link. The whole group can be shown on and off at once. It should be declared global at initialization time to be useful. + class GMarkerGroup + include MappingObject + attr_accessor :active, :markers, :markers_by_id + + def initialize(active = true , markers = nil) + @active = active + @markers = [] + @markers_by_id = {} + if markers.is_a?(Array) + @markers = markers + elsif markers.is_a?(Hash) + @markers_by_id = markers + end + end + + def create + "new GMarkerGroup(#{MappingObject.javascriptify_variable(@active)},#{MappingObject.javascriptify_variable(@markers)},#{MappingObject.javascriptify_variable(@markers_by_id)})" + end + end + + #Can be used to implement a clusterer, similar to the clusterer below, except that there is more stuff to manage explicitly byt the programmer (but this is also more flexible). See the README for usage esamples. + class GMarkerManager + include MappingObject + + attr_accessor :map,:options,:managed_markers + + #options can be :border_padding, :max_zoom, :track_markers and :managed_markers: managed_markers must be an array of ManagedMarker objects + def initialize(map, options = {}) + @map = map + @managed_markers = Array(options.delete(:managed_markers)) #[] if nil + @options = options + end + + def create + puts @options.inspect + "addMarkersToManager(new GMarkerManager(#{MappingObject.javascriptify_variable(@map)},#{MappingObject.javascriptify_variable(@options)}),#{MappingObject.javascriptify_variable(@managed_markers)})" + end + + end + + #A set of similarly managed markers: They share the same minZoom and maxZoom. + class ManagedMarker + include MappingObject + + attr_accessor :markers,:min_zoom, :max_zoom + + def initialize(markers,min_zoom,max_zoom = nil) + @markers = markers + @min_zoom = min_zoom + @max_zoom = max_zoom + end + + def create + "new ManagedMarker(#{MappingObject.javascriptify_variable(@markers)},#{MappingObject.javascriptify_variable(@min_zoom)},#{MappingObject.javascriptify_variable(@max_zoom)})" + end + + end + + #Makes the link with the Clusterer2 library by Jef Poskanzer (slightly modified though). Is a GOverlay making clusters out of its GMarkers, so that GMarkers very close to each other appear as one when the zoom is low. When the zoom gets higher, the individual markers are drawn. + class Clusterer + include MappingObject + attr_accessor :markers,:icon, :max_visible_markers, :grid_size, :min_markers_per_cluster , :max_lines_per_info_box + + def initialize(markers = [], options = {}) + @markers = markers + @icon = options[:icon] || GIcon::DEFAULT + @max_visible_markers = options[:max_visible_markers] || 150 + @grid_size = options[:grid_size] || 5 + @min_markers_per_cluster = options[:min_markers_per_cluster] || 5 + @max_lines_per_info_box = options[:max_lines_per_info_box] || 10 + end + + def create + js_marker = '[' + @markers.collect do |marker| + add_description(marker) + end.join(",") + ']' + + "new Clusterer(#{js_marker},#{MappingObject.javascriptify_variable(@icon)},#{MappingObject.javascriptify_variable(@max_visible_markers)},#{MappingObject.javascriptify_variable(@grid_size)},#{MappingObject.javascriptify_variable(@min_markers_per_cluster)},#{MappingObject.javascriptify_variable(@max_lines_per_info_box)})" + end + + private + def add_description(marker) + "addDescriptionToMarker(#{MappingObject.javascriptify_variable(marker)},#{MappingObject.javascriptify_variable(marker.options[:description] || marker.options[:title] || '')})" + end + end + + #Makes the link with the MGeoRSS extension by Mikel Maron (a bit modified though). It lets you overlay on top of Google Maps the items present in a RSS feed that has GeoRss data. This data can be either in W3C Geo vocabulary or in the GeoRss Simple format. See http://georss.org to know more about GeoRss. + class GeoRssOverlay + include MappingObject + attr_accessor :url, :proxy, :icon, :options + + #You can pass the following options: + #- :icon: An icon for the items of the feed. Defaults to the classic red balloon icon. + #- :proxy: An URL on your server where fetching the RSS feed will be taken care of. + #- :list_div: In case you want a list of all the markers, with a link on which you can click in order to display the info on the marker, use this option to indicate the ID of the div (that you must place yourself). + #- :list_item_class: class of the DIV containing each item of the list. Ignored if option :list_div is not set. + #- :limit: Maximum number of items to display on the map. + #- :content_div: Instead of having an info window appear, indicates the ID of the DIV where this info should be displayed. + def initialize(url, options = {}) + @url = url + @icon = options.delete(:icon) || GIcon::DEFAULT + @proxy = options.delete(:proxy) || Variable::UNDEFINED + @options = options + end + + def create + "new GeoRssOverlay(#{MappingObject.javascriptify_variable(@url)},#{MappingObject.javascriptify_variable(@icon)},#{MappingObject.javascriptify_variable(@proxy)},#{MappingObject.javascriptify_variable(@options)})" + end + end + + end +end diff --git a/lib/gm_plugin/point.rb b/lib/gm_plugin/point.rb index bcde4e2..d99b595 100644 --- a/lib/gm_plugin/point.rb +++ b/lib/gm_plugin/point.rb @@ -1,34 +1,34 @@ -module Ym4r - module GmPlugin - #A point in pixel coordinates - class GPoint < Struct.new(:x,:y) - include MappingObject - def create - "new GPoint(#{x},#{y})" - end - end - #A rectangular that contains all the pixel points passed as arguments - class GBounds - include MappingObject - attr_accessor :points - #Accepts both an array of GPoint and an array of 2-element arrays - def initialize(points) - if !points.empty? and points[0].is_a?(Array) - @points = points.collect { |pt| GPoint.new(pt[0],pt[1]) } - else - @points = points - end - end - def create - "new GBounds([#{@points.map { |pt| pt.to_javascript}.join(",")}])" - end - end - #A size object, in pixel space - class GSize < Struct.new(:width,:height) - include MappingObject - def create - "new GSize(#{width},#{height})" - end - end - end -end +module Ym4r + module GmPlugin + #A point in pixel coordinates + class GPoint < Struct.new(:x,:y) + include MappingObject + def create + "new GPoint(#{x},#{y})" + end + end + #A rectangular that contains all the pixel points passed as arguments + class GBounds + include MappingObject + attr_accessor :points + #Accepts both an array of GPoint and an array of 2-element arrays + def initialize(points) + if !points.empty? and points[0].is_a?(Array) + @points = points.collect { |pt| GPoint.new(pt[0],pt[1]) } + else + @points = points + end + end + def create + "new GBounds([#{@points.map { |pt| pt.to_javascript}.join(",")}])" + end + end + #A size object, in pixel space + class GSize < Struct.new(:width,:height) + include MappingObject + def create + "new GSize(#{width},#{height})" + end + end + end +end diff --git a/lib/ym4r_gm.rb b/lib/ym4r_gm.rb index fdb2f90..ae16bd9 100644 --- a/lib/ym4r_gm.rb +++ b/lib/ym4r_gm.rb @@ -1,12 +1,12 @@ -require 'gm_plugin/key' -require 'gm_plugin/mapping' -require 'gm_plugin/map' -require 'gm_plugin/control' -require 'gm_plugin/point' -require 'gm_plugin/overlay' -require 'gm_plugin/layer' -require 'gm_plugin/helper' -require 'gm_plugin/geocoding' -require 'gm_plugin/encoder' - -include Ym4r::GmPlugin +require 'gm_plugin/key' +require 'gm_plugin/mapping' +require 'gm_plugin/map' +require 'gm_plugin/control' +require 'gm_plugin/point' +require 'gm_plugin/overlay' +require 'gm_plugin/layer' +require 'gm_plugin/helper' +require 'gm_plugin/geocoding' +require 'gm_plugin/encoder' + +include Ym4r::GmPlugin