/*! * GMaps.js * http://hpneo.github.com/gmaps/ * * Copyright 2012, Gustavo Leon * Released under the MIT License. */ var GMaps = (function(global) { "use strict"; var doc = document; var getElementById = function(id, context) { var ele if('jQuery' in global && context){ ele = $("#"+id.replace('#', ''), context)[0] }else{ ele = doc.getElementById(id.replace('#', '')); }; return ele; }; var GMaps = function(options) { var self = this; window.context_menu = {}; if(typeof(options.div)=='string'){ this.div = getElementById(options.div, options.context); }else{this.div = options.div;}; this.div.style.width = this.div.clientWidth || options.width; this.div.style.height = this.div.clientHeight || options.height; this.controls = []; this.overlays = []; this.layers = []; this.markers = []; this.polylines = []; this.routes = []; this.polygons = []; this.infoWindow = null; this.overlay_div = null; this.zoom = options.zoom || 15; //'Hybrid', 'Roadmap', 'Satellite' or 'Terrain' var mapType; if (options.mapType) { mapType = google.maps.MapTypeId[options.mapType.toUpperCase()]; } else { mapType = google.maps.MapTypeId.ROADMAP; } var map_center = new google.maps.LatLng(options.lat, options.lng); delete options.div; delete options.lat; delete options.lng; delete options.mapType; delete options.width; delete options.height; var zoomControlOpt = options.zoomControlOpt || { style: 'DEFAULT', position: 'TOP_LEFT' }; var zoomControl = options.zoomControl || true, zoomControlStyle = zoomControlOpt.style || 'DEFAULT', zoomControlPosition = zoomControlOpt.position || 'TOP_LEFT', panControl = options.panControl || true, mapTypeControl = options.mapTypeControl || true, scaleControl = options.scaleControl || true, streetViewControl = options.streetViewControl || true, overviewMapControl = overviewMapControl || true; var map_base_options = { zoom: this.zoom, center: map_center, mapTypeId: mapType, panControl: panControl, zoomControl: zoomControl, zoomControlOptions: { style: google.maps.ZoomControlStyle[zoomControlStyle], // DEFAULT LARGE SMALL position: google.maps.ControlPosition[zoomControlPosition] }, mapTypeControl: mapTypeControl, scaleControl: scaleControl, streetViewControl: streetViewControl, overviewMapControl: overviewMapControl }; var map_options = extend_object(map_base_options, options); this.map = new google.maps.Map(this.div, map_options); // Context menus var buildContextMenuHTML = function(control, e) { var html = ''; var options = window.context_menu[control]; for (var i in options){ if (options.hasOwnProperty(i)){ var option = options[i]; html += '
  • ' + option.title + '
  • '; } } if(!getElementById('gmaps_context_menu')) return; var context_menu_element = getElementById('gmaps_context_menu'); context_menu_element.innerHTML = html; var context_menu_items = context_menu_element.getElementsByTagName('a'); var context_menu_items_count = context_menu_items.length; for(var i=0;i 0) { if(options.paths[0].length > 0) { options.paths = array_map(options.paths, arrayToLatLng); } } var polygon = new google.maps.Polygon(options); var polygon_events = ['click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'rightclick']; for (var ev = 0; ev < polygon_events.length; ev++) { (function(object, name) { google.maps.event.addListener(object, name, function(e){ if (options[name]) options[name].apply(this, [e]); }); })(polygon, polygon_events[ev]); } this.polygons.push(polygon); return polygon; }; this.getFromFusionTables = function(options) { var events = options.events; delete options.events; var fusion_tables_options = options; var layer = new google.maps.FusionTablesLayer(fusion_tables_options); for (var ev in events) { (function(object, name) { google.maps.event.addListener(object, name, function(e){ events[name].apply(this, [e]); }); })(layer, ev); } this.layers.push(layer); return layer; }; this.loadFromFusionTables = function(options) { var layer = this.getFromFusionTables(options); layer.setMap(this.map); return layer; }; this.getFromKML = function(options) { var url = options.url; var events = options.events; delete options.url; delete options.events; var kml_options = options; var layer = new google.maps.KmlLayer(url, kml_options); for (var ev in events) { (function(object, name) { google.maps.event.addListener(object, name, function(e){ events[name].apply(this, [e]); }); })(layer, ev); } this.layers.push(layer); return layer; }; this.loadFromKML = function(options) { var layer = this.getFromKML(options); layer.setMap(this.map); return layer; }; // Services var travelMode, unitSystem; this.getRoutes = function(options) { switch (options.travelMode) { case 'bicycling': travelMode = google.maps.TravelMode.BICYCLING; break; case 'driving': travelMode = google.maps.TravelMode.DRIVING; break; // case 'walking': default: travelMode = google.maps.TravelMode.WALKING; break; } if (options.unitSystem === 'imperial') { unitSystem = google.maps.UnitSystem.IMPERIAL; } else { unitSystem = google.maps.UnitSystem.METRIC; } var base_options = { avoidHighways: false, avoidTolls: false, optimizeWaypoints: false, waypoints: [] }; var request_options = extend_object(base_options, options); request_options.origin = new google.maps.LatLng(options.origin[0], options.origin[1]); request_options.destination = new google.maps.LatLng(options.destination[0], options.destination[1]); request_options.travelMode = travelMode; request_options.unitSystem = unitSystem; delete request_options.callback; var self = this; var service = new google.maps.DirectionsService(); service.route(request_options, function(result, status) { if (status === google.maps.DirectionsStatus.OK) { for (var r in result.routes) { if (result.routes.hasOwnProperty(r)) { self.routes.push(result.routes[r]); } } } if (options.callback) { options.callback(self.routes); } }); }; this.getElevations = function(options) { options = extend_object({ locations: [], path : false, samples : 256 }, options); if(options.locations.length > 0) { if(options.locations[0].length > 0) { options.locations = array_map(options.locations, arrayToLatLng); } } var callback = options.callback; delete options.callback; var service = new google.maps.ElevationService(); //location request if (!options.path) { delete options.path; delete options.samples; service.getElevationForLocations(options, function(result, status){ if (callback && typeof(callback) === "function") { callback(result, status); } }); //path request } else { var pathRequest = { path : options.locations, samples : options.samples }; service.getElevationAlongPath(pathRequest, function(result, status){ if (callback && typeof(callback) === "function") { callback(result, status); } }); } }; this.removePolylines = function(){ var index; for(index in this.polylines){ this.polylines[index].setMap(null); } this.polylines = []; } // Alias for the method "drawRoute" this.cleanRoute = this.removePolylines; this.drawRoute = function(options) { var self = this; this.getRoutes({ origin: options.origin, destination: options.destination, travelMode: options.travelMode, waypoints : options.waypoints, callback: function(e) { if (e.length > 0) { self.drawPolyline({ path: e[e.length - 1].overview_path, strokeColor: options.strokeColor, strokeOpacity: options.strokeOpacity, strokeWeight: options.strokeWeight }); if (options.callback) { options.callback(e[e.length - 1]); } } } }); }; this.travelRoute = function(options) { if (options.origin && options.destination) { this.getRoutes({ origin: options.origin, destination: options.destination, travelMode: options.travelMode, waypoints : options.waypoints, callback: function(e) { if (e.length > 0 && options.step) { var route = e[e.length - 1]; if (route.legs.length > 0) { var steps = route.legs[0].steps; for (var i=0, step; step=steps[i]; i++) { step.step_number = i; options.step(step); } } } } }); } else if (options.route) { if (options.route.legs.length > 0) { var steps = options.route.legs[0].steps; for (var i=0, step; step=steps[i]; i++) { step.step_number = i; options.step(step); } } } }; this.drawSteppedRoute = function(options) { if (options.origin && options.destination) { this.getRoutes({ origin: options.origin, destination: options.destination, travelMode: options.travelMode, callback: function(e) { if (e.length > 0 && options.step) { var route = e[e.length - 1]; if (route.legs.length > 0) { var steps = route.legs[0].steps; for (var i=0, step; step=steps[i]; i++) { step.step_number = i; self.drawPolyline({ path: step.path, strokeColor: options.strokeColor, strokeOpacity: options.strokeOpacity, strokeWeight: options.strokeWeight }); options.step(step); } } } } }); } else if (options.route) { if (options.route.legs.length > 0) { var steps = options.route.legs[0].steps; for (var i=0, step; step=steps[i]; i++) { step.step_number = i; self.drawPolyline({ path: step.path, strokeColor: options.strokeColor, strokeOpacity: options.strokeOpacity, strokeWeight: options.strokeWeight }); options.step(step); } } } }; // Geofence this.checkGeofence = function(lat, lng, fence) { return fence.containsLatLng(new google.maps.LatLng(lat, lng)); }; this.checkMarkerGeofence = function(marker, outside_callback) { if (marker.fences) { for (var i=0, fence; fence=marker.fences[i]; i++) { var pos = marker.getPosition(); if (!self.checkGeofence(pos.lat(), pos.lng(), fence)) { outside_callback(marker, fence); } } } }; }; GMaps.Route = function(options) { this.map = options.map; this.route = options.route; this.step_count = 0; this.steps = this.route.legs[0].steps; this.steps_length = this.steps.length; this.polyline = this.map.drawPolyline({ path: new google.maps.MVCArray(), strokeColor: options.strokeColor, strokeOpacity: options.strokeOpacity, strokeWeight: options.strokeWeight }).getPath(); this.back = function() { if (this.step_count > 0) { this.step_count--; var path = this.route.legs[0].steps[this.step_count].path; for (var p in path){ if (path.hasOwnProperty(p)){ this.polyline.pop(); } } } }; this.forward = function() { if (this.step_count < this.steps_length) { var path = this.route.legs[0].steps[this.step_count].path; for (var p in path){ if (path.hasOwnProperty(p)){ this.polyline.push(path[p]); } } this.step_count++; } }; }; // Geolocation (Modern browsers only) GMaps.geolocate = function(options) { if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(function(position) { options.success(position); if (options.always) { options.always(); } }, function(error) { options.error(error); if (options.always) { options.always(); } }, options.options); } else { options.not_supported(); if (options.always) { options.always(); } } }; // Geocoding GMaps.geocode = function(options) { this.geocoder = new google.maps.Geocoder(); var callback = options.callback; if (options.lat && options.lng) { options.latLng = new google.maps.LatLng(options.lat, options.lng); } delete options.lat; delete options.lng; delete options.callback; this.geocoder.geocode(options, function(results, status) { callback(results, status); }); }; // Static maps GMaps.staticMapURL = function(options){ var parameters = []; var data; var static_root = 'http://maps.googleapis.com/maps/api/staticmap'; if (options.url){ static_root = options.url; delete options.url; } static_root += '?'; var markers = options.markers; delete options.markers; if (!markers && options.marker){ markers = [options.marker]; delete options.marker; } var polyline = options.polyline; delete options.polyline; /** Map options **/ if (options.center){ parameters.push('center=' + options.center); delete options.center; } else if (options.address){ parameters.push('center=' + options.address); delete options.address; } else if (options.lat){ parameters.push(['center=', options.lat, ',', options.lng].join('')); delete options.lat; delete options.lng; } else if (options.visible){ var visible = encodeURI(options.visible.join('|')); parameters.push('visible=' + visible); } var size = options.size; if (size){ if (size.join){ size = size.join('x'); } delete options.size; } else { size = '630x300'; } parameters.push('size=' + size); if (!options.zoom){ options.zoom = 15; } var sensor = options.hasOwnProperty('sensor') ? !!options.sensor : true; delete options.sensor; parameters.push('sensor=' + sensor); for (var param in options){ if (options.hasOwnProperty(param)){ parameters.push(param + '=' + options[param]); } } /** Markers **/ if (markers){ var marker, loc; for (var i=0; data=markers[i]; i++){ marker = []; if (data.size && data.size !== 'normal'){ marker.push('size:' + data.size); } else if (data.icon){ marker.push('icon:' + encodeURI(data.icon)); } if (data.color){ marker.push('color:' + data.color.replace('#', '0x')); } if (data.label){ marker.push('label:' + data.label[0].toUpperCase()); } loc = (data.address ? data.address : data.lat + ',' + data.lng); if (marker.length || i === 0){ marker.push(loc); marker = marker.join('|'); parameters.push('markers=' + encodeURI(marker)); } // New marker without styles else { marker = parameters.pop() + encodeURI('|' + loc); parameters.push(marker); } } } /** Polylines **/ function parseColor(color, opacity){ if (color[0] === '#'){ color = color.replace('#', '0x'); if (opacity){ opacity = parseFloat(opacity); opacity = Math.min(1, Math.max(opacity, 0)); if (opacity === 0){ return '0x00000000'; } opacity = (opacity * 255).toString(16); if (opacity.length === 1){ opacity += opacity; } color = color.slice(0,8) + opacity; } } return color; } if (polyline){ data = polyline; polyline = []; if (data.strokeWeight){ polyline.push('weight:' + parseInt(data.strokeWeight, 10)); } if (data.strokeColor){ var color = parseColor(data.strokeColor, data.strokeOpacity); polyline.push('color:' + color); } if (data.fillColor){ var fillcolor = parseColor(data.fillColor, data.fillOpacity); polyline.push('fillcolor:' + fillcolor); } var path = data.path; if (path.join){ for (var j=0, pos; pos=path[j]; j++){ polyline.push(pos.join(',')); } } else { polyline.push('enc:' + path); } polyline = polyline.join('|'); parameters.push('path=' + encodeURI(polyline)); } parameters = parameters.join('&'); return static_root + parameters; }; //========================== // Polygon containsLatLng // https://github.com/tparkin/Google-Maps-Point-in-Polygon // Poygon getBounds extension - google-maps-extensions // http://code.google.com/p/google-maps-extensions/source/browse/google.maps.Polygon.getBounds.js if (!google.maps.Polygon.prototype.getBounds) { google.maps.Polygon.prototype.getBounds = function(latLng) { var bounds = new google.maps.LatLngBounds(); var paths = this.getPaths(); var path; for (var p = 0; p < paths.getLength(); p++) { path = paths.getAt(p); for (var i = 0; i < path.getLength(); i++) { bounds.extend(path.getAt(i)); } } return bounds; }; } // Polygon containsLatLng - method to determine if a latLng is within a polygon google.maps.Polygon.prototype.containsLatLng = function(latLng) { // Exclude points outside of bounds as there is no way they are in the poly var bounds = this.getBounds(); if (bounds !== null && !bounds.contains(latLng)) { return false; } // Raycast point in polygon method var inPoly = false; var numPaths = this.getPaths().getLength(); for (var p = 0; p < numPaths; p++) { var path = this.getPaths().getAt(p); var numPoints = path.getLength(); var j = numPoints - 1; for (var i = 0; i < numPoints; i++) { var vertex1 = path.getAt(i); var vertex2 = path.getAt(j); if (vertex1.lng() < latLng.lng() && vertex2.lng() >= latLng.lng() || vertex2.lng() < latLng.lng() && vertex1.lng() >= latLng.lng()) { if (vertex1.lat() + (latLng.lng() - vertex1.lng()) / (vertex2.lng() - vertex1.lng()) * (vertex2.lat() - vertex1.lat()) < latLng.lat()) { inPoly = !inPoly; } } j = i; } } return inPoly; }; google.maps.LatLngBounds.prototype.containsLatLng = function(latLng) { return this.contains(latLng); }; google.maps.Marker.prototype.setFences = function(fences) { this.fences = fences; }; google.maps.Marker.prototype.addFence = function(fence) { this.fences.push(fence); }; return GMaps; }(this)); var arrayToLatLng = function(coords) { return new google.maps.LatLng(coords[0], coords[1]); } var extend_object = function(obj, new_obj) { if(obj === new_obj) return obj; for(var name in new_obj){ obj[name] = new_obj[name]; } return obj; }; var array_map = function(array, callback) { if (Array.prototype.map && array.map === Array.prototype.map) { return array.map(callback); } else { var array_return = []; var array_length = array.length; for(var i = 0; i < array_length; i++) { array_return.push(callback(array[i])); } return array_return; } }