From d3ab78e2c68725c09e50dddabd8b87d08a48f596 Mon Sep 17 00:00:00 2001 From: alexs Date: Fri, 9 Aug 2013 13:20:49 +1000 Subject: [PATCH] Assets for gmap4rails gem. --- .../gmaps4rails/gmaps4rails.base.js.coffee | 444 ++++++++++++++++++ .../gmaps4rails/gmaps4rails.bing.js.coffee | 174 +++++++ .../gmaps4rails.googlemaps.js.coffee | 339 +++++++++++++ .../gmaps4rails.mapquest.js.coffee | 145 ++++++ .../gmaps4rails.openlayers.js.coffee | 261 ++++++++++ app/assets/stylesheets/search/gmaps4rails.css | 19 + 6 files changed, 1382 insertions(+) create mode 100644 app/assets/javascripts/search/gmaps4rails/gmaps4rails.base.js.coffee create mode 100644 app/assets/javascripts/search/gmaps4rails/gmaps4rails.bing.js.coffee create mode 100644 app/assets/javascripts/search/gmaps4rails/gmaps4rails.googlemaps.js.coffee create mode 100644 app/assets/javascripts/search/gmaps4rails/gmaps4rails.mapquest.js.coffee create mode 100644 app/assets/javascripts/search/gmaps4rails/gmaps4rails.openlayers.js.coffee create mode 100644 app/assets/stylesheets/search/gmaps4rails.css diff --git a/app/assets/javascripts/search/gmaps4rails/gmaps4rails.base.js.coffee b/app/assets/javascripts/search/gmaps4rails/gmaps4rails.base.js.coffee new file mode 100644 index 0000000000..684215a438 --- /dev/null +++ b/app/assets/javascripts/search/gmaps4rails/gmaps4rails.base.js.coffee @@ -0,0 +1,444 @@ +Gmaps = {} + +Gmaps.triggerOldOnload = -> + Gmaps.oldOnload() if typeof(Gmaps.oldOnload) == 'function' + +Gmaps.loadMaps = -> + #loop through all variable names. + #there should only be maps inside so it trigger their load function + for key, value of Gmaps + searchLoadIncluded = key.search(/load/) + if searchLoadIncluded == -1 + load_function_name = "load_" + key + Gmaps[load_function_name]() + +window.Gmaps = Gmaps + +class @Gmaps4Rails + + constructor: -> + #map config + @map = null #DEPRECATED: will still contain a copy of serviceObject below as transition + @serviceObject = null #contains the map we're working on + @visibleInfoWindow = null #contains the current opened infowindow + @userLocation = null #contains user's location if geolocalization was performed and successful + + #empty slots + @geolocationSuccess = -> false #triggered when geolocation succeeds. Can be customized. + @geolocationFailure = -> false #triggered when geolocation fails. If customized, must be like= function(navigator_handles_geolocation){} where 'navigator_handles_geolocation' is a boolean + @callback = -> false #to let user set a custom callback function + @customClusterer = -> false #to let user set custom clusterer pictures + @infobox = -> false #to let user use custom infoboxes + @jsTemplate = false #to let user create infowindows client side + + @default_map_options = + id: 'map' + draggable: true + detect_location: false # should the browser attempt to use geolocation detection features of HTML5? + center_on_user: false # centers map on the location detected through the browser + center_latitude: 0 + center_longitude: 0 + zoom: 7 + maxZoom: null + minZoom: null + auto_adjust : true # adjust the map to the markers if set to true + auto_zoom: true # zoom given by auto-adjust + bounds: [] # adjust map to these limits. Should be [{"lat": , "lng": }] + raw: {} # raw json to pass additional options + + @default_markers_conf = + #Marker config + title: "" + #MarkerImage config + picture : "" + width: 22 + length: 32 + draggable: false # how to modify: <%= gmaps( "markers" => { "data" => @object.to_gmaps4rails, "options" => { "draggable" => true }}) %> + #clustering config + do_clustering: false # do clustering if set to true + randomize: false # Google maps can't display two markers which have the same coordinates. This randomizer enables to prevent this situation from happening. + max_random_distance: 100 # in meters. Each marker coordinate could be altered by this distance in a random direction + list_container: null # id of the ul that will host links to all markers + offset: 0 # used when adding_markers to an existing map. Because new markers are concated with previous one, offset is here to prevent the existing from being re-created. + raw: {} # raw json to pass additional options + + #Stored variables + @markers = [] # contains all markers. A marker contains the following: {"description": , "longitude": , "title":, "latitude":, "picture": "", "width": "", "length": "", "sidebar": "", "serviceObject": google_marker} + @boundsObject = null # contains current bounds from markers, polylines etc... + @polygons = [] # contains raw data, array of arrays (first element could be a hash containing options) + @polylines = [] # contains raw data, array of arrays (first element could be a hash containing options) + @circles = [] # contains raw data, array of hash + @markerClusterer = null # contains all marker clusterers + @markerImages = [] + + #Polyline Styling + @polylines_conf = #default style for polylines + strokeColor: "#FF0000" + strokeOpacity: 1 + strokeWeight: 2 + clickable: false + zIndex: null + + #tnitializes the map + initialize : -> + @serviceObject = @createMap() + @map = @serviceObject #beware, soon deprecated + if (@map_options.detect_location == true or @map_options.center_on_user == true) + @findUserLocation(this) + #resets sidebar if needed + @resetSidebarContent() + + findUserLocation : (map_object) -> + if (navigator.geolocation) + #try to retrieve user's position + positionSuccessful = (position) -> + map_object.userLocation = map_object.createLatLng(position.coords.latitude, position.coords.longitude) + #change map's center to focus on user's geoloc if asked + if(map_object.map_options.center_on_user == true) + map_object.centerMapOnUser() + map_object.geolocationSuccess() + positionFailure = -> + map_object.geolocationFailure(true) + + navigator.geolocation.getCurrentPosition( positionSuccessful, positionFailure) + else + #failure but the navigator doesn't handle geolocation + map_object.geolocationFailure(false) + + + #//////////////////////////////////////////////////// + #//////////////////// DIRECTIONS //////////////////// + #//////////////////////////////////////////////////// + + create_direction : -> + directionsDisplay = new google.maps.DirectionsRenderer() + directionsService = new google.maps.DirectionsService() + + directionsDisplay.setMap(@serviceObject) + #display panel only if required + if @direction_conf.display_panel + directionsDisplay.setPanel(document.getElementById(@direction_conf.panel_id)) + + directionsDisplay.setOptions + suppressMarkers: false + suppressInfoWindows: false + suppressPolylines: false + + request = + origin: @direction_conf.origin + destination: @direction_conf.destination + waypoints: @direction_conf.waypoints + optimizeWaypoints: @direction_conf.optimizeWaypoints + unitSystem: google.maps.DirectionsUnitSystem[@direction_conf.unitSystem] + avoidHighways: @direction_conf.avoidHighways + avoidTolls: @direction_conf.avoidTolls + region: @direction_conf.region + travelMode: google.maps.DirectionsTravelMode[@direction_conf.travelMode] + language: "en" + + directionsService.route request, (response, status) -> + if (status == google.maps.DirectionsStatus.OK) + directionsDisplay.setDirections(response) + + #//////////////////////////////////////////////////// + #///////////////////// CIRCLES ////////////////////// + #//////////////////////////////////////////////////// + + #Loops through all circles + #Loops through all circles and draws them + create_circles : -> + for circle in @circles + @create_circle circle + + create_circle : (circle) -> + #by convention, default style configuration could be integrated in the first element + if circle == @circles[0] + @circles_conf.strokeColor = circle.strokeColor if circle.strokeColor? + @circles_conf.strokeOpacity = circle.strokeOpacity if circle.strokeOpacity? + @circles_conf.strokeWeight = circle.strokeWeight if circle.strokeWeight? + @circles_conf.fillColor = circle.fillColor if circle.fillColor? + @circles_conf.fillOpacity = circle.fillOpacity if circle.fillOpacity? + + if circle.lat? and circle.lng? + # always check if a config is given, if not, use defaults + # NOTE: is there a cleaner way to do this? Maybe a hash merge of some sort? + newCircle = new google.maps.Circle + center: @createLatLng(circle.lat, circle.lng) + strokeColor: circle.strokeColor || @circles_conf.strokeColor + strokeOpacity: circle.strokeOpacity || @circles_conf.strokeOpacity + strokeWeight: circle.strokeWeight || @circles_conf.strokeWeight + fillOpacity: circle.fillOpacity || @circles_conf.fillOpacity + fillColor: circle.fillColor || @circles_conf.fillColor + clickable: circle.clickable || @circles_conf.clickable + zIndex: circle.zIndex || @circles_conf.zIndex + radius: circle.radius + + circle.serviceObject = newCircle + newCircle.setMap(@serviceObject) + + # clear circles + clear_circles : -> + for circle in @circles + @clear_circle circle + + clear_circle : (circle) -> + circle.serviceObject.setMap(null) + + hide_circles : -> + for circle in @circles + @hide_circle circle + + hide_circle : (circle) -> + circle.serviceObject.setMap(null) + + show_circles : -> + for circle in @circles + @show_circle @circle + + show_circle : (circle) -> + circle.serviceObject.setMap(@serviceObject) + + #//////////////////////////////////////////////////// + #///////////////////// POLYGONS ///////////////////// + #//////////////////////////////////////////////////// + + #polygons is an array of arrays. It loops. + create_polygons : -> + for polygon in @polygons + @create_polygon(polygon) + + #creates a single polygon, triggered by create_polygons + create_polygon : (polygon) -> + polygon_coordinates = [] + + #Polygon points are in an Array, that's why looping is necessary + for point in polygon + latlng = @createLatLng(point.lat, point.lng) + polygon_coordinates.push(latlng) + #first element of an Array could contain specific configuration for this particular polygon. If no config given, use default + if point == polygon[0] + strokeColor = point.strokeColor || @polygons_conf.strokeColor + strokeOpacity = point.strokeOpacity || @polygons_conf.strokeOpacity + strokeWeight = point.strokeWeight || @polygons_conf.strokeWeight + fillColor = point.fillColor || @polygons_conf.fillColor + fillOpacity = point.fillOpacity || @polygons_conf.fillOpacity + clickable = point.clickable || @polygons_conf.clickable + + #Construct the polygon + new_poly = new google.maps.Polygon + paths: polygon_coordinates + strokeColor: strokeColor + strokeOpacity: strokeOpacity + strokeWeight: strokeWeight + fillColor: fillColor + fillOpacity: fillOpacity + clickable: clickable + map: @serviceObject + + #save polygon in list + polygon.serviceObject = new_poly + + + + #//////////////////////////////////////////////////// + #///////////////////// MARKERS ////////////////////// + #//////////////////////////////////////////////////// + + #creates, clusterizes and adjusts map + create_markers : -> + @createServiceMarkersFromMarkers() + @clusterize() + + #create google.maps Markers from data provided by user + createServiceMarkersFromMarkers : -> + for marker, index in @markers + if not @markers[index].serviceObject? + #extract options, test if value passed or use default + Lat = @markers[index].lat + Lng = @markers[index].lng + + #alter coordinates if randomize is true + if @markers_conf.randomize + LatLng = @randomize(Lat, Lng) + #retrieve coordinates from the array + Lat = LatLng[0] + Lng = LatLng[1] + + #save object + @markers[index].serviceObject = @createMarker + "marker_picture": if @markers[index].picture then @markers[index].picture else @markers_conf.picture + "marker_width": if @markers[index].width then @markers[index].width else @markers_conf.width + "marker_height": if @markers[index].height then @markers[index].height else @markers_conf.length + "marker_title": if @markers[index].title then @markers[index].title else null + "marker_anchor": if @markers[index].marker_anchor then @markers[index].marker_anchor else null + "shadow_anchor": if @markers[index].shadow_anchor then @markers[index].shadow_anchor else null + "shadow_picture": if @markers[index].shadow_picture then @markers[index].shadow_picture else null + "shadow_width": if @markers[index].shadow_width then @markers[index].shadow_width else null + "shadow_height": if @markers[index].shadow_height then @markers[index].shadow_height else null + "marker_draggable": if @markers[index].draggable then @markers[index].draggable else @markers_conf.draggable + "rich_marker": if @markers[index].rich_marker then @markers[index].rich_marker else null + "zindex": if @markers[index].zindex then @markers[index].zindex else null + "Lat": Lat + "Lng": Lng + "index": index + + #add infowindowstuff if enabled + @createInfoWindow(@markers[index]) + #create sidebar if enabled + @createSidebar(@markers[index]) + + @markers_conf.offset = @markers.length + + #creates Image Anchor Position or return null if nothing passed + createImageAnchorPosition : (anchorLocation) -> + if (anchorLocation == null) + return null + else + return @createPoint(anchorLocation[0], anchorLocation[1]) + + + #replace old markers with new markers on an existing map + replaceMarkers : (new_markers, adjustBounds = true) -> + @clearMarkers() + #reset previous markers + @markers = new Array + #reset current bounds + @boundsObject = @createLatLngBounds() if adjustBounds + #reset sidebar content if exists + @resetSidebarContent() + #add new markers + @markers_conf.offset = 0 + @addMarkers(new_markers, adjustBounds) + + #add new markers to on an existing map + addMarkers : (new_markers, adjustBounds = true) -> + #update the list of markers to take into account + @markers = @markers.concat(new_markers) + #put markers on the map + @create_markers() + @adjustMapToBounds() if adjustBounds + + #//////////////////////////////////////////////////// + #///////////////////// SIDEBAR ////////////////////// + #//////////////////////////////////////////////////// + + #//creates sidebar + createSidebar : (marker_container) -> + if (@markers_conf.list_container) + ul = document.getElementById(@markers_conf.list_container) + li = document.createElement('li') + aSel = document.createElement('a') + aSel.href = 'javascript:void(0);' + html = if marker_container.sidebar? then marker_container.sidebar else "Marker" + aSel.innerHTML = html + currentMap = this + aSel.onclick = @sidebar_element_handler(currentMap, marker_container.serviceObject, 'click') + li.appendChild(aSel) + ul.appendChild(li) + + #moves map to marker clicked + open infowindow + sidebar_element_handler : (currentMap, marker, eventType) -> + return () -> + currentMap.map.panTo(marker.position) + google.maps.event.trigger(marker, eventType) + + + resetSidebarContent : -> + if @markers_conf.list_container isnt null + ul = document.getElementById(@markers_conf.list_container) + ul.innerHTML = "" + + #//////////////////////////////////////////////////// + #////////////////// MISCELLANEOUS /////////////////// + #//////////////////////////////////////////////////// + + #to make the map fit the different LatLng points + adjustMapToBounds : -> + #FIRST_STEP: retrieve all bounds + #create the bounds object only if necessary + if @map_options.auto_adjust or @map_options.bounds isnt null + @boundsObject = @createLatLngBounds() + + #if autodjust is true, must get bounds from markers polylines etc... + if @map_options.auto_adjust + #from markers + @extendBoundsWithMarkers() + + #from polylines: + @updateBoundsWithPolylines() + + #from polygons: + @updateBoundsWithPolygons() + + #from circles + @updateBoundsWithCircles() + + #in every case, I've to take into account the bounds set up by the user + @extendMapBounds() + + #SECOND_STEP: ajust the map to the bounds + @adaptMapToBounds() + + #//////////////////////////////////////////////////// + #/////////////////// POLYLINES ////////////////////// + #//////////////////////////////////////////////////// + + #replace old markers with new markers on an existing map + replacePolylines : (new_polylines) -> + #reset previous polylines and kill them from map + @destroy_polylines() + #set new polylines + @polylines = new_polylines + #create + @create_polylines() + #.... and adjust map boundaries + @adjustMapToBounds() + + destroy_polylines : -> + for polyline in @polylines + #delete polylines from map + polyline.serviceObject.setMap(null) + #empty array + @polylines = [] + + #polylines is an array of arrays. It loops. + create_polylines : -> + for polyline in @polylines + @create_polyline polyline + + #//////////////////////////////////////////////////// + #///////////////// Basic functions ////////////////// + #///////////////////tests coded////////////////////// + + #//basic function to check existence of a variable + exists : (var_name) -> + return (var_name != "" and typeof var_name != "undefined") + + + #randomize + randomize : (Lat0, Lng0) -> + #distance in meters between 0 and max_random_distance (positive or negative) + dx = @markers_conf.max_random_distance * @random() + dy = @markers_conf.max_random_distance * @random() + Lat = parseFloat(Lat0) + (180/Math.PI)*(dy/6378137) + Lng = parseFloat(Lng0) + ( 90/Math.PI)*(dx/6378137)/Math.cos(Lat0) + return [Lat, Lng] + + mergeObjectWithDefault : (object1, object2) -> + copy_object1 = {} + for key, value of object1 + copy_object1[key] = value + + for key, value of object2 + unless copy_object1[key]? + copy_object1[key] = value + return copy_object1 + + mergeWithDefault : (objectName) -> + default_object = @["default_" + objectName] + object = @[objectName] + @[objectName] = @mergeObjectWithDefault(object, default_object) + return true + + #gives a value between -1 and 1 + random : -> return(Math.random() * 2 -1) diff --git a/app/assets/javascripts/search/gmaps4rails/gmaps4rails.bing.js.coffee b/app/assets/javascripts/search/gmaps4rails/gmaps4rails.bing.js.coffee new file mode 100644 index 0000000000..9eb53a6b76 --- /dev/null +++ b/app/assets/javascripts/search/gmaps4rails/gmaps4rails.bing.js.coffee @@ -0,0 +1,174 @@ +###################################################################################################### +############################################## Bing Maps ########################################## +###################################################################################################### + +#// http://wiki.openstreetmap.org/wiki/OpenLayers +#// http://openlayers.org/dev/examples +#//http://docs.openlayers.org/contents.html + +class @Gmaps4RailsBing extends Gmaps4Rails + + constructor: -> + super + @map_options = + type: "road" # aerial, auto, birdseye, collinsBart, mercator, ordnanceSurvey, road + @markers_conf = + infobox: "description" #description or htmlContent + + @mergeWithDefault("map_options") + @mergeWithDefault("markers_conf") + + #//////////////////////////////////////////////////// + #/////////////// Basic Objects ////////////// + #//////////////////////////////////////////////////// + + getMapType: -> + switch @map_options.type + when "road" then return Microsoft.Maps.MapTypeId.road + when "aerial" then return Microsoft.Maps.MapTypeId.aerial + when "auto" then return Microsoft.Maps.MapTypeId.auto + when "birdseye" then return Microsoft.Maps.MapTypeId.birdseye + when "collinsBart" then return Microsoft.Maps.MapTypeId.collinsBart + when "mercator" then return Microsoft.Maps.MapTypeId.mercator + when "ordnanceSurvey" then return Microsoft.Maps.MapTypeId.ordnanceSurvey + else return Microsoft.Maps.MapTypeId.auto + + createPoint: (lat, lng) -> + return new Microsoft.Maps.Point(lat, lng) + + createLatLng:(lat, lng) -> + return new Microsoft.Maps.Location(lat, lng) + + createLatLngBounds: -> + + createMap: -> + return new Microsoft.Maps.Map(document.getElementById(@map_options.id), { + credentials: @map_options.provider_key, + mapTypeId: @getMapType(), + center: @createLatLng(@map_options.center_latitude, @map_options.center_longitude), + zoom: @map_options.zoom + }) + + createSize: (width, height) -> + return new google.maps.Size(width, height) + + #//////////////////////////////////////////////////// + #////////////////////// Markers ///////////////////// + #//////////////////////////////////////////////////// + + createMarker: (args) -> + markerLatLng = @createLatLng(args.Lat, args.Lng) + marker + #// Marker sizes are expressed as a Size of X,Y + if args.marker_picture == "" + marker = new Microsoft.Maps.Pushpin(@createLatLng(args.Lat, args.Lng), { + draggable: args.marker_draggable, + anchor: @createImageAnchorPosition(args.Lat, args.Lng), + text: args.marker_title + } + ); + else + marker = new Microsoft.Maps.Pushpin(@createLatLng(args.Lat, args.Lng), { + draggable: args.marker_draggable, + anchor: @createImageAnchorPosition(args.Lat, args.Lng), + icon: args.marker_picture, + height: args.marker_height, + text: args.marker_title, + width: args.marker_width + } + ); + @addToMap(marker) + return marker + + #// clear markers + clearMarkers: -> + for marker in @markers + @clearMarker marker + + clearMarker: (marker) -> + @removeFromMap(marker.serviceObject) + + #//show and hide markers + showMarkers: -> + for marker in @markers + @showMarker marker + + showMarker: (marker) -> + marker.serviceObject.setOptions({ visible: true }) + + hideMarkers: -> + for marker in @markers + @hideMarker marker + + hideMarker: (marker) -> + marker.serviceObject.setOptions({ visible: false }) + + extendBoundsWithMarkers: -> + locationsArray = [] + for marker in @markers + locationsArray.push(marker.serviceObject.getLocation()) + @boundsObject = Microsoft.Maps.LocationRect.fromLocations(locationsArray) + + #//////////////////////////////////////////////////// + #/////////////////// Clusterer ////////////////////// + #//////////////////////////////////////////////////// + + createClusterer: (markers_array) -> + + clearClusterer: -> + + #//creates clusters + clusterize: -> + + #//////////////////////////////////////////////////// + #/////////////////// INFO WINDOW //////////////////// + #//////////////////////////////////////////////////// + + #// creates infowindows + createInfoWindow: (marker_container) -> + if marker_container.description? + #//create the infowindow + if @markers_conf.infobox == "description" + marker_container.info_window = new Microsoft.Maps.Infobox(marker_container.serviceObject.getLocation(), { description: marker_container.description, visible: false, showCloseButton: true}) + else + marker_container.info_window = new Microsoft.Maps.Infobox(marker_container.serviceObject.getLocation(), { htmlContent: marker_container.description, visible: false}) + + #//add the listener associated + currentMap = this + Microsoft.Maps.Events.addHandler(marker_container.serviceObject, 'click', @openInfoWindow(currentMap, marker_container.info_window)) + @addToMap(marker_container.info_window) + + openInfoWindow: (currentMap, infoWindow) -> + return -> + # Close the latest selected marker before opening the current one. + if currentMap.visibleInfoWindow + currentMap.visibleInfoWindow.setOptions({ visible: false }) + infoWindow.setOptions({ visible:true }) + currentMap.visibleInfoWindow = infoWindow + + #//////////////////////////////////////////////////// + #/////////////////// Other methods ////////////////// + #//////////////////////////////////////////////////// + + fitBounds: -> + @serviceObject.setView({bounds: @boundsObject}) + + addToMap: (object)-> + @serviceObject.entities.push(object) + + removeFromMap: (object)-> + @serviceObject.entities.remove(object) + + centerMapOnUser: -> + @serviceObject.setView({ center: @userLocation}) + + updateBoundsWithPolylines: ()-> + + updateBoundsWithPolygons: ()-> + + updateBoundsWithCircles: ()-> + + extendMapBounds :-> + + adaptMapToBounds: -> + @fitBounds() \ No newline at end of file diff --git a/app/assets/javascripts/search/gmaps4rails/gmaps4rails.googlemaps.js.coffee b/app/assets/javascripts/search/gmaps4rails/gmaps4rails.googlemaps.js.coffee new file mode 100644 index 0000000000..ed52ddc15a --- /dev/null +++ b/app/assets/javascripts/search/gmaps4rails/gmaps4rails.googlemaps.js.coffee @@ -0,0 +1,339 @@ +####################################################################################################### +############################################## Google maps ########################################## +####################################################################################################### + +class @Gmaps4RailsGoogle extends Gmaps4Rails + + constructor: -> + super + #Map settings + @map_options = + disableDefaultUI: false + disableDoubleClickZoom: false + type: "ROADMAP" # HYBRID, ROADMAP, SATELLITE, TERRAIN + + #markers + info styling + @markers_conf = + clusterer_gridSize: 50 + clusterer_maxZoom: 5 + custom_cluster_pictures: null + custom_infowindow_class: null + + @mergeWithDefault("map_options") + @mergeWithDefault("markers_conf") + + @kml_options = + clickable: true + preserveViewport: false + suppressInfoWindows: false + + #Polygon Styling + @polygons_conf = # default style for polygons + strokeColor: "#FFAA00" + strokeOpacity: 0.8 + strokeWeight: 2 + fillColor: "#000000" + fillOpacity: 0.35 + clickable: false + + #Circle Styling + @circles_conf = #default style for circles + fillColor: "#00AAFF" + fillOpacity: 0.35 + strokeColor: "#FFAA00" + strokeOpacity: 0.8 + strokeWeight: 2 + clickable: false + zIndex: null + + #Direction Settings + @direction_conf = + panel_id: null + display_panel: false + origin: null + destination: null + waypoints: [] #[{location: "toulouse,fr", stopover: true}, {location: "Clermont-Ferrand, fr", stopover: true}] + optimizeWaypoints: false + unitSystem: "METRIC" #IMPERIAL + avoidHighways: false + avoidTolls: false + region: null + travelMode: "DRIVING" #WALKING, BICYCLING + + #//////////////////////////////////////////////////// + #/////////////// Basic Objects ////////////// + #//////////////////////////////////////////////////// + + createPoint : (lat, lng) -> + return new google.maps.Point(lat, lng) + + createLatLng : (lat, lng) -> + return new google.maps.LatLng(lat, lng) + + createLatLngBounds : -> + return new google.maps.LatLngBounds() + + createMap : -> + defaultOptions = + maxZoom: @map_options.maxZoom + minZoom: @map_options.minZoom + zoom: @map_options.zoom + center: @createLatLng(@map_options.center_latitude, @map_options.center_longitude) + mapTypeId: google.maps.MapTypeId[@map_options.type] + mapTypeControl: @map_options.mapTypeControl + disableDefaultUI: @map_options.disableDefaultUI + disableDoubleClickZoom: @map_options.disableDoubleClickZoom + draggable: @map_options.draggable + + mergedOptions = @mergeObjectWithDefault @map_options.raw, defaultOptions + + return new google.maps.Map document.getElementById(@map_options.id), mergedOptions + + + createMarkerImage : (markerPicture, markerSize, origin, anchor, scaledSize) -> + return new google.maps.MarkerImage(markerPicture, markerSize, origin, anchor, scaledSize) + + createSize : (width, height) -> + return new google.maps.Size(width, height) + + #//////////////////////////////////////////////////// + #////////////////////// Markers ///////////////////// + #//////////////////////////////////////////////////// + + createMarker : (args) -> + markerLatLng = @createLatLng(args.Lat, args.Lng) + #Marker sizes are expressed as a Size of X,Y + if args.marker_picture == "" and args.rich_marker == null + defaultOptions = {position: markerLatLng, map: @serviceObject, title: args.marker_title, draggable: args.marker_draggable, zIndex: args.zindex} + mergedOptions = @mergeObjectWithDefault @markers_conf.raw, defaultOptions + return new google.maps.Marker mergedOptions + + if (args.rich_marker != null) + return new RichMarker({ + position: markerLatLng + map: @serviceObject + draggable: args.marker_draggable + content: args.rich_marker + flat: if args.marker_anchor == null then false else args.marker_anchor[1] + anchor: if args.marker_anchor == null then 0 else args.marker_anchor[0] + zIndex: args.zindex + }) + + #default behavior + #calculate MarkerImage anchor location + imageAnchorPosition = @createImageAnchorPosition args.marker_anchor + shadowAnchorPosition = @createImageAnchorPosition args.shadow_anchor + #create or retrieve existing MarkerImages + markerImage = @createOrRetrieveImage(args.marker_picture, args.marker_width, args.marker_height, imageAnchorPosition) + shadowImage = @createOrRetrieveImage(args.shadow_picture, args.shadow_width, args.shadow_height, shadowAnchorPosition) + defaultOptions = {position: markerLatLng, map: @serviceObject, icon: markerImage, title: args.marker_title, draggable: args.marker_draggable, shadow: shadowImage, zIndex: args.zindex} + mergedOptions = @mergeObjectWithDefault @markers_conf.raw, defaultOptions + return new google.maps.Marker mergedOptions + + #checks if obj is included in arr Array and returns the position or false + includeMarkerImage : (arr, obj) -> + for object, index in arr + return index if object.url == obj + return false + + #checks if MarkerImage exists before creating a new one + #returns a MarkerImage or false if ever something wrong is passed as argument + createOrRetrieveImage : (currentMarkerPicture, markerWidth, markerHeight, imageAnchorPosition) -> + return null if (currentMarkerPicture == "" or currentMarkerPicture == null ) + + test_image_index = @includeMarkerImage(@markerImages, currentMarkerPicture) + switch test_image_index + when false + markerImage = @createMarkerImage(currentMarkerPicture, @createSize(markerWidth, markerHeight), null, imageAnchorPosition, null ) + @markerImages.push(markerImage) + return markerImage + break + else + return @markerImages[test_image_index] if typeof test_image_index == 'number' + return false + + #clear markers + clearMarkers : -> + for marker in @markers + @clearMarker marker + + #show and hide markers + showMarkers : -> + for marker in @markers + @showMarker marker + + hideMarkers : -> + for marker in @markers + @hideMarker marker + + clearMarker : (marker) -> + marker.serviceObject.setMap(null) + + showMarker : (marker) -> + marker.serviceObject.setVisible(true) + + hideMarker : (marker) -> + marker.serviceObject.setVisible(false) + + extendBoundsWithMarkers : -> + for marker in @markers + @boundsObject.extend(marker.serviceObject.position) + + #//////////////////////////////////////////////////// + #/////////////////// Clusterer ////////////////////// + #//////////////////////////////////////////////////// + + createClusterer : (markers_array) -> + return new MarkerClusterer( @serviceObject, markers_array, { maxZoom: @markers_conf.clusterer_maxZoom, gridSize: @markers_conf.clusterer_gridSize, styles: @customClusterer() }) + + clearClusterer : -> + @markerClusterer.clearMarkers() + + #creates clusters + clusterize : -> + if @markers_conf.do_clustering == true + #first clear the existing clusterer if any + @clearClusterer() if @markerClusterer != null + + markers_array = new Array + for marker in @markers + markers_array.push(marker.serviceObject) + + @markerClusterer = @createClusterer(markers_array) + + #//////////////////////////////////////////////////// + #/////////////////// INFO WINDOW //////////////////// + #//////////////////////////////////////////////////// + + #// creates infowindows + createInfoWindow : (marker_container) -> + if typeof(@jsTemplate) == "function" or marker_container.description? + marker_container.description = @jsTemplate(marker_container) if typeof(@jsTemplate) == "function" + if @markers_conf.custom_infowindow_class != null + #creating custom infowindow + boxText = document.createElement("div") + boxText.setAttribute("class", @markers_conf.custom_infowindow_class) #to customize + boxText.innerHTML = marker_container.description + marker_container.infowindow = new InfoBox(@infobox(boxText)) + currentMap = this + google.maps.event.addListener(marker_container.serviceObject, 'click', @openInfoWindow(currentMap, marker_container.infowindow, marker_container.serviceObject)) + else + #create default infowindow + marker_container.infowindow = new google.maps.InfoWindow({content: marker_container.description }) + #add the listener associated + currentMap = this + google.maps.event.addListener(marker_container.serviceObject, 'click', @openInfoWindow(currentMap, marker_container.infowindow, marker_container.serviceObject)) + + openInfoWindow : (currentMap, infoWindow, marker) -> + return -> + # Close the latest selected marker before opening the current one. + currentMap.visibleInfoWindow.close() if currentMap.visibleInfoWindow != null + infoWindow.open(currentMap.serviceObject, marker) + currentMap.visibleInfoWindow = infoWindow + + #//////////////////////////////////////////////////// + #///////////////// KML ////////////////// + #//////////////////////////////////////////////////// + + createKmlLayer : (kml) -> + kml_options = kml.options || {} + kml_options = @mergeObjectWithDefault(kml_options, @kml_options) + kml = new google.maps.KmlLayer( kml.url, kml_options) + kml.setMap(@serviceObject) + return kml + + #//////////////////////////////////////////////////// + #/////////////////// POLYLINES ////////////////////// + #//////////////////////////////////////////////////// + + #creates a single polyline, triggered by create_polylines + create_polyline : (polyline) -> + polyline_coordinates = [] + + #2 cases here, either we have a coded array of LatLng or we have an Array of LatLng + for element in polyline + #if we have a coded array + if element.coded_array? + decoded_array = new google.maps.geometry.encoding.decodePath(element.coded_array) + #loop through every point in the array + for point in decoded_array + polyline_coordinates.push(point) + + #or we have an array of latlng + else + #by convention, a single polyline could be customized in the first array or it uses default values + if element == polyline[0] + strokeColor = element.strokeColor || @polylines_conf.strokeColor + strokeOpacity = element.strokeOpacity || @polylines_conf.strokeOpacity + strokeWeight = element.strokeWeight || @polylines_conf.strokeWeight + clickable = element.clickable || @polylines_conf.clickable + zIndex = element.zIndex || @polylines_conf.zIndex + + #add latlng if positions provided + if element.lat? && element.lng? + latlng = @createLatLng(element.lat, element.lng) + polyline_coordinates.push(latlng) + + # Construct the polyline + new_poly = new google.maps.Polyline + path: polyline_coordinates + strokeColor: strokeColor + strokeOpacity: strokeOpacity + strokeWeight: strokeWeight + clickable: clickable + zIndex: zIndex + + #save polyline + polyline.serviceObject = new_poly + new_poly.setMap(@serviceObject) + + + updateBoundsWithPolylines: ()-> + for polyline in @polylines + polyline_points = polyline.serviceObject.latLngs.getArray()[0].getArray() + for point in polyline_points + @boundsObject.extend point + + #//////////////////////////////////////////////////// + #///////////////// KML ////////////////// + #//////////////////////////////////////////////////// + + create_kml : -> + for kml in @kml + kml.serviceObject = @createKmlLayer kml + + #//////////////////////////////////////////////////// + #/////////////////// Other methods ////////////////// + #//////////////////////////////////////////////////// + + fitBounds : -> + @serviceObject.fitBounds(@boundsObject) unless @boundsObject.isEmpty() + + centerMapOnUser : -> + @serviceObject.setCenter(@userLocation) + + updateBoundsWithPolygons: ()-> + for polygon in @polygons + polygon_points = polygon.serviceObject.latLngs.getArray()[0].getArray() + for point in polygon_points + @boundsObject.extend point + + updateBoundsWithCircles: ()-> + for circle in @circles + @boundsObject.extend(circle.serviceObject.getBounds().getNorthEast()) + @boundsObject.extend(circle.serviceObject.getBounds().getSouthWest()) + + extendMapBounds: ()-> + for bound in @map_options.bounds + #create points from bounds provided + @boundsObject.extend @createLatLng(bound.lat, bound.lng) + + adaptMapToBounds:()-> + #if autozoom is false, take user info into account + if !@map_options.auto_zoom + map_center = @boundsObject.getCenter() + @map_options.center_latitude = map_center.lat() + @map_options.center_longitude = map_center.lng() + @serviceObject.setCenter(map_center) + else + @fitBounds() diff --git a/app/assets/javascripts/search/gmaps4rails/gmaps4rails.mapquest.js.coffee b/app/assets/javascripts/search/gmaps4rails/gmaps4rails.mapquest.js.coffee new file mode 100644 index 0000000000..08fca694d0 --- /dev/null +++ b/app/assets/javascripts/search/gmaps4rails/gmaps4rails.mapquest.js.coffee @@ -0,0 +1,145 @@ +####################################################################################################### +############################################## Map Quest ############################################# +####################################################################################################### +# http://developer.mapquest.com/web/documentation/sdk/javascript/v7.0/api/MQA.Poi.html + +class @Gmaps4RailsMapquest extends Gmaps4Rails + + constructor: -> + super + #Map settings + @map_options = {type: "map"} #map type (map, sat, hyb) + @markers_conf = {} + @mergeWithDefault "markers_conf" + @mergeWithDefault "map_options" + + #//////////////////////////////////////////////////// + #/////////////// Basic Objects ////////////// + #//////////////////////////////////////////////////// + + createPoint: (lat, lng) -> + return new MQA.Poi({lat: lat, lng: lng}) + + createLatLng: (lat, lng) -> + return {lat: lat, lng: lng} + + createLatLngBounds: -> + + createMap: -> + map = new MQA.TileMap( #// Constructs an instance of MQA.TileMap + document.getElementById(@map_options.id), #//the id of the element on the page you want the map to be added into + @map_options.zoom, #//intial zoom level of the map + {lat: @map_options.center_latitude, lng: @map_options.center_longitude}, + @map_options.type) #//map type (map, sat, hyb) + + MQA.withModule('zoomcontrol3', (-> + map.addControl( + new MQA.LargeZoomControl3(), + new MQA.MapCornerPlacement(MQA.MapCorner.TOP_LEFT) + ) + )) + return map + + createMarkerImage: (markerPicture, markerSize, origin, anchor, scaledSize) -> + + #//////////////////////////////////////////////////// + #////////////////////// Markers ///////////////////// + #//////////////////////////////////////////////////// + + createMarker: (args)-> + marker = new MQA.Poi( {lat: args.Lat, lng: args.Lng} ) + + if args.marker_picture != "" + icon = new MQA.Icon(args.marker_picture, args.marker_height, args.marker_width) + marker.setIcon(icon) + if args.marker_anchor != null + marker.setBias({x: args.marker_anchor[0], y: args.marker_anchor[1]}) + + if args.shadow_picture != "" + icon = new MQA.Icon(args.shadow_picture, args.shadow_height, args.shadow_width) + marker.setShadow(icon) + + if args.shadow_anchor != null + marker.setShadowOffset({x: args.shadow_anchor[0], y: args.shadow_anchor[1]}) + + @addToMap marker + return marker + + + #// clear markers + clearMarkers: -> + for marker in markers + @clearMarker marker + + #//show and hide markers + showMarkers: -> + for marker in markers + @showMarker marker + + hideMarkers: -> + for marker in markers + @hideMarker marker + + clearMarker: (marker) -> + @removeFromMap(marker.serviceObject) + + showMarker: (marker) -> + #// marker.serviceObject + + hideMarker: (marker) -> + #// marker.serviceObject + + extendBoundsWithMarkers: -> + if @markers.length >=2 + @boundsObject = new MQA.RectLL(@markers[0].serviceObject.latLng, @markers[1].serviceObject.latLng) + for marker in @markers + @boundsObject.extend marker.serviceObject.latLng + + #//////////////////////////////////////////////////// + #/////////////////// Clusterer ////////////////////// + #//////////////////////////////////////////////////// + + createClusterer: (markers_array) -> + + clearClusterer: -> + + #//creates clusters + clusterize: -> + + #//////////////////////////////////////////////////// + #/////////////////// INFO WINDOW //////////////////// + #//////////////////////////////////////////////////// + + #// creates infowindows + createInfoWindow: (marker_container) -> + marker_container.serviceObject.setInfoTitleHTML(marker_container.description) + #//TODO: how to disable the mouseover display when using setInfoContentHTML? + #//marker_container.serviceObject.setInfoContentHTML(marker_container.description); + + #//////////////////////////////////////////////////// + #/////////////////// Other methods ////////////////// + #//////////////////////////////////////////////////// + + fitBounds: -> + @serviceObject.zoomToRect @boundsObject if @markers.length >=2 + @serviceObject.setCenter @markers[0].serviceObject.latLng if @markers.length == 1 + + centerMapOnUser: -> + @serviceObject.setCenter @userLocation + + addToMap: (object) -> + @serviceObject.addShape object + + removeFromMap: (object)-> + @serviceObject.removeShape object + + updateBoundsWithPolylines: ()-> + + updateBoundsWithPolygons: ()-> + + updateBoundsWithCircles: ()-> + + extendMapBounds :-> + + adaptMapToBounds: -> + @fitBounds() \ No newline at end of file diff --git a/app/assets/javascripts/search/gmaps4rails/gmaps4rails.openlayers.js.coffee b/app/assets/javascripts/search/gmaps4rails/gmaps4rails.openlayers.js.coffee new file mode 100644 index 0000000000..1cddc04e39 --- /dev/null +++ b/app/assets/javascripts/search/gmaps4rails/gmaps4rails.openlayers.js.coffee @@ -0,0 +1,261 @@ +####################################################################################################### +############################################## Open Layers ########################################## +####################################################################################################### + +#// http://wiki.openstreetmap.org/wiki/OpenLayers +#// http://openlayers.org/dev/examples +#//http://docs.openlayers.org/contents.html + +class @Gmaps4RailsOpenlayers extends Gmaps4Rails + + constructor: -> + super + @map_options = {} + @mergeWithDefault "map_options" + @markers_conf = {} + @mergeWithDefault "markers_conf" + + @openMarkers = null + @markersLayer = null + @markersControl = null + @polylinesLayer = null + + #//////////////////////////////////////////////////// + #/////////////// Basic Objects //////////////////// + #//////////////////////////////////////////////////// + + createPoint: (lat, lng)-> + + createLatLng: (lat, lng)-> + return new OpenLayers.LonLat(lng, lat).transform(new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection("EPSG:900913")) # transform from WGS 1984 to Spherical Mercator Projection + + createAnchor: (offset)-> + return null if offset == null + return new OpenLayers.Pixel(offset[0], offset[1]) + + createSize: (width, height)-> + return new OpenLayers.Size(width, height) + + createLatLngBounds: -> + return new OpenLayers.Bounds() + + createMap: -> + #//todo add customization: kind of map and other map options + map = new OpenLayers.Map(@map_options.id) + map.addLayer(new OpenLayers.Layer.OSM()) + map.setCenter(@createLatLng(@map_options.center_latitude, @map_options.center_longitude), #// Center of the map + @map_options.zoom) #// Zoom level + return map + + #//////////////////////////////////////////////////// + #////////////////////// Markers ///////////////////// + #//////////////////////////////////////////////////// + #//http://openlayers.org/dev/examples/marker-shadow.html + createMarker: (args) -> + style_mark = OpenLayers.Util.extend({}, OpenLayers.Feature.Vector.style['default']) + style_mark.fillOpacity = 1 + + #//creating markers' dedicated layer + if (@markersLayer == null) + @markersLayer = new OpenLayers.Layer.Vector("Markers", null) + @serviceObject.addLayer(@markersLayer) + #//TODO move? + @markersLayer.events.register("featureselected", @markersLayer, @onFeatureSelect) + @markersLayer.events.register("featureunselected", @markersLayer, @onFeatureUnselect) + @markersControl = new OpenLayers.Control.SelectFeature(@markersLayer) + @serviceObject.addControl(@markersControl) + @markersControl.activate() + #//showing default pic if none available + if args.marker_picture == "" + #style_mark.graphicWidth = 24 + style_mark.graphicHeight = 30 + style_mark.externalGraphic = "http://openlayers.org/dev/img/marker-blue.png" + #//creating custom pic + else + style_mark.graphicWidth = args.marker_width + style_mark.graphicHeight = args.marker_height + style_mark.externalGraphic = args.marker_picture + #//adding anchor if any + if args.marker_anchor != null + style_mark.graphicXOffset = args.marker_anchor[0] + style_mark.graphicYOffset = args.marker_anchor[1] + #//adding shadow if any + if args.shadow_picture != "" + style_mark.backgroundGraphic = args.shadow_picture + style_mark.backgroundWidth = args.shadow_width + style_mark.backgroundHeight = args.shadow_height + #//adding shadow's anchor if any + if args.shadow_anchor != null + style_mark.backgroundXOffset = args.shadow_anchor[0] + style_mark.backgroundYOffset = args.shadow_anchor[1] + + style_mark.graphicTitle = args.marker_title + marker = new OpenLayers.Feature.Vector( + new OpenLayers.Geometry.Point(args.Lng, args.Lat), + null, + style_mark) + #//changing coordinates so that it actually appears on the map! + marker.geometry.transform(new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection("EPSG:900913")) + #//adding layer to the map + @markersLayer.addFeatures([marker]) + + return marker + + #//clear markers + clearMarkers: -> + @clearMarkersLayerIfExists() + @markersLayer = null + @boundsObject = new OpenLayers.Bounds() + + clearMarkersLayerIfExists: -> + @serviceObject.removeLayer(@markersLayer) if @markersLayer != null and @serviceObject.getLayer(@markersLayer.id) != null + + extendBoundsWithMarkers: -> + console.log "here" + for marker in @markers + @boundsObject.extend(@createLatLng(marker.lat,marker.lng)) + + #//////////////////////////////////////////////////// + #/////////////////// Clusterer ////////////////////// + #//////////////////////////////////////////////////// + #//too ugly to be considered valid :( + + createClusterer: (markers_array)-> + options = + pointRadius: "${radius}" + fillColor: "#ffcc66" + fillOpacity: 0.8 + strokeColor: "#cc6633" + strokeWidth: "${width}" + strokeOpacity: 0.8 + funcs = + context: + width: (feature) -> + return (feature.cluster) ? 2 : 1 + radius: (feature) -> + pix = 2 + pix = Math.min(feature.attributes.count, 7) + 2 if feature.cluster + return pix + + style = new OpenLayers.Style options, funcs + + strategy = new OpenLayers.Strategy.Cluster() + + clusters = new OpenLayers.Layer.Vector "Clusters", + strategies: [strategy] + styleMap: new OpenLayers.StyleMap + "default": style + "select": + fillColor: "#8aeeef" + strokeColor: "#32a8a9" + + @clearMarkersLayerIfExists() + @serviceObject.addLayer(clusters) + clusters.addFeatures(markers_array) + return clusters + + clusterize: -> + + if @markers_conf.do_clustering == true + #//first clear the existing clusterer if any + if @markerClusterer != null + @clearClusterer() + markers_array = new Array + for marker in @markers + markers_array.push(marker.serviceObject) + @markerClusterer = @createClusterer markers_array + + clearClusterer: -> + @serviceObject.removeLayer @markerClusterer + + #//////////////////////////////////////////////////// + #/////////////////// INFO WINDOW //////////////////// + #//////////////////////////////////////////////////// + + #// creates infowindows + createInfoWindow: (marker_container) -> + marker_container.serviceObject.infoWindow = marker_container.description if marker_container.description? + + onPopupClose: (evt) -> + #// 'this' is the popup. + @markersControl.unselect @feature + + onFeatureSelect: (evt) -> + feature = evt.feature + popup = new OpenLayers.Popup.FramedCloud("featurePopup", + feature.geometry.getBounds().getCenterLonLat(), + new OpenLayers.Size(300,200), + feature.infoWindow, + null, true, @onPopupClose) + feature.popup = popup + popup.feature = feature + @map.addPopup popup + + onFeatureUnselect: (evt) -> + feature = evt.feature + if feature.popup + #//popup.feature = null; + @map.removePopup feature.popup + feature.popup.destroy() + feature.popup = null + + #//////////////////////////////////////////////////// + #/////////////////// POLYLINES ////////////////////// + #//////////////////////////////////////////////////// + + create_polyline : (polyline) -> + + if(@polylinesLayer == null) + @polylinesLayer = new OpenLayers.Layer.Vector("Polylines", null) + @serviceObject.addLayer(@polylinesLayer) + @polylinesLayer.events.register("featureselected", @polylinesLayer, @onFeatureSelect) + @polylinesLayer.events.register("featureunselected", @polylinesLayer, @onFeatureUnselect) + @polylinesControl = new OpenLayers.Control.DrawFeature(@polylinesLayer, OpenLayers.Handler.Path) + @serviceObject.addControl(@polylinesControl) + + polyline_coordinates = [] + + for element in polyline + #by convention, a single polyline could be customized in the first array or it uses default values + if element == polyline[0] + strokeColor = element.strokeColor || @polylines_conf.strokeColor + strokeOpacity = element.strokeOpacity || @polylines_conf.strokeOpacity + strokeWeight = element.strokeWeight || @polylines_conf.strokeWeight + clickable = element.clickable || @polylines_conf.clickable + zIndex = element.zIndex || @polylines_conf.zIndex + + #add latlng if positions provided + if element.lat? && element.lng? + latlng = new OpenLayers.Geometry.Point(element.lng, element.lat) + polyline_coordinates.push(latlng) + + line_points = new OpenLayers.Geometry.LineString(polyline_coordinates); + line_style = { strokeColor: strokeColor, strokeOpacity: strokeOpacity, strokeWidth: strokeWeight }; + + polyline = new OpenLayers.Feature.Vector(line_points, null, line_style); + polyline.geometry.transform(new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection("EPSG:900913")) + + @polylinesLayer.addFeatures([polyline]) + + return polyline + + updateBoundsWithPolylines: ()-> + + updateBoundsWithPolygons: ()-> + + updateBoundsWithCircles: ()-> + + # #//////////////////////////////////////////////////// + # #/////////////////// Other methods ////////////////// + # #//////////////////////////////////////////////////// + + fitBounds: -> + @serviceObject.zoomToExtent(@boundsObject, true) + + centerMapOnUser: -> + @serviceObject.setCenter @userLocation + + extendMapBounds :-> + + adaptMapToBounds: -> + @fitBounds() diff --git a/app/assets/stylesheets/search/gmaps4rails.css b/app/assets/stylesheets/search/gmaps4rails.css new file mode 100644 index 0000000000..4fd99ae8f2 --- /dev/null +++ b/app/assets/stylesheets/search/gmaps4rails.css @@ -0,0 +1,19 @@ +.map_container { + padding: 6px; + border-width: 1px; + border-style: solid; + border-color: #ccc #ccc #999 #ccc; + -webkit-box-shadow: rgba(64, 64, 64, 0.5) 0 2px 5px; + -moz-box-shadow: rgba(64, 64, 64, 0.5) 0 2px 5px; + box-shadow: rgba(64, 64, 64, 0.1) 0 2px 5px; + width: 645px; +} + +.gmaps4rails_map { + width: 630px; + height: 530px; +} + +.map_container img { + max-width: none; +}