function SfmbMapsGoogle(opts) { var divObject = opts.divObject, polyPointCallback = opts.polyPointCallback, getPolygonCallback = opts.getPolygonCallback, landmarkCallback = opts.landmarkCallback, mapLoaded = opts.mapLoaded, streetViewOpen = opts.streetViewOpen; google.maps.fastCommandCallback = opts.fastCommandCallback; this.div = divObject; var infoWindowModel = new InfoWindowModel; /* [{point, marker, label}...] */ var markers = []; var polygons = []; var polys = []; // global polygon used between [get/delete/new]polygon functions var editingPolygon = null; var newOnly = false; var mapType = getMapTypeFromString(opts.mapType); var mapOptions = { zoom: 1, center: new google.maps.LatLng(0, 0), mapTypeId: mapType }; // we keep only one marker at a time var infoBubble = new InfoBubble({ map: map, disableAutoPan: true, padding:4, content : new InfoWindowView(_.extend({model:infoWindowModel}, opts)).el }); infoBubble.close(); google.maps.event.addListener(infoBubble,'closeclick', function(){ // console.log('Info window closed...'); }); var map = new google.maps.Map( document.getElementById(divObject.getAttribute('id')), mapOptions); map.setOptions({draggableCursor:'default'}); //silently update map properties center so re-center will work properly google.maps.event.addListener(map, 'dragend', function() { opts.properties.attributes.center = [map.getCenter().lat(), map.getCenter().lng()]; } ); google.maps.event.addListenerOnce(map,'tilesloaded', function(){mapLoaded();}) var panorama = new google.maps.StreetViewPanorama(map.getDiv(), {enableCloseButton: true, visible:false}); panorama.setOptions({enableCloseButton: true, visible:false}); map.setStreetView(panorama); google.maps.event.addListener(panorama, 'visible_changed', function() { streetViewOpen(panorama.getVisible()); }); google.maps.openStreetView = function(){ map.getStreetView().setVisible(true); streetViewOpen(true); log("am deschis street view "+map.getStreetView()) } // close the marker bubble and geocode bubble, add point to editing polygon google.maps.event.addListener(map, 'click', function(event) { infoBubble.close(); if (markerGeocode != null) { markerGeocode.setMap(null); markerGeocode = null; } if (newOnly && editingPolygon != null) { if (!editingPolygon.polygon.getPath()) { editingPolygon.polygon.setPath([event.latLng]); } else { editingPolygon.polygon.getPath().push(event.latLng); } } if (!newOnly && landmarkCallback) { landmarkCallback({latitude:event.latLng.lat(), longitude:event.latLng.lng()}); } }); /* Resize the map to specified position; */ SfmbMapsGoogle.prototype.resize = function(height, width) { this.div.style.height = height + 'px'; this.div.style.width = width + 'px'; google.maps.event.trigger(map, "resize"); } /* Put the markers on map @params pointsArray: [{id, latitude, longitude, icon, description}...] setAutozoom: boolean */ SfmbMapsGoogle.prototype.putPoints = function(pointsArray, setAutozoom) { if (pointsArray.length == 0) return; // console.dir(pointsArray); var bounds = new google.maps.LatLngBounds(); var self = this; // console.dir(markers); // console.dir(pointsArray); //remove markers that are not in pointsArray markers.filter(function(m){ return (m.dataset == pointsArray[0].dataset && !_.find(pointsArray,{id:m.id})); }).forEach(function(p){ self.removePoint(p); }); pointsArray.forEach(function(e) { // if (infoWindowModel.get('id') == e.id) infoWindowModel.mergePoint(e); bounds.extend(new google.maps.LatLng(e.latitude, e.longitude)); var existingMarker = _.findWhere(markers, {id:e.id}); if (e.id != "" && !existingMarker) markers.push(addMarkerWithLabel(e)); if (existingMarker) { var samePosition = normDecimals(existingMarker.marker.getPosition().lat()) == normDecimals(e.latitude) && normDecimals(e.longitude) == normDecimals(existingMarker.marker.getPosition().lng()) var shouldUpdate = !samePosition || (existingMarker.marker.icon.url != e.icon) || (existingMarker.point.isInCall != e.isInCall) || (existingMarker.point.address != e.address); // console.log('===========Is in call changed ? existing = '+ existingMarker.point.isInCall+ " new one ="+e.isInCall); if (shouldUpdate) updateMarker(existingMarker,e); } }); if (setAutozoom) { map.fitBounds(bounds); if (pointsArray.length == 1) map.setZoom(map.getZoom()-3); } }; //leave only n decimals in float function normDecimals(float, n){ n = n || 8; return parseFloat(parseFloat(float).toFixed(n)); }; //'private' variable and functions var isLegacyIcons = false; //this is where Adrian will intervene for future updates to map icons function getNewIconsSize(iconPath){ var color = "#287aab"; var anchor = new google.maps.Point(16,48); var reg = /_[0-9A-F]{6}_[sml]\./; //test for color and size in icon name var size = "default_size"; var sizes = { s : new google.maps.Size(-6,-40), m : new google.maps.Size(-2,-33), l : new google.maps.Size(0,-24), default_size : new google.maps.Size(0,-25) }; if (iconPath && reg.test(iconPath)) { var ref = iconPath.split(/[_\.]/).slice(-3,-1); color = '#'+ref[0]; size = ref[1]; } // console.log(color + ' ' + size); return { labelOffset : sizes[size], labelColor : color, imageMarkerAnchor : anchor } } function updateMarker(marker, point){ var newPosition = new google.maps.LatLng(point.latitude, point.longitude); marker.marker.setPosition(newPosition); marker.marker.setIcon(point.icon); marker.label.setPosition(newPosition); // console.dir(marker.label); // var sizes = getNewIconsSize(point.icon); // marker.label.setContent(getLabelContent(sizes.labelColor, point.id)); //marker.label.setPixelOffset(sizes.labelOffset); marker.point = point; if (infoWindowModel.id == point.id){ infoWindowModel.mergePoint(point); if (infoWindowModel.isOpened()) map.setCenter(newPosition); } } function getLabelContent(color, name){ return '

' + name + '

' } function addMarkerWithLabel(point) { var lat = point.latitude; var lon = point.longitude; var imgFolder = "images/"; imgFolder = ""; var imageMarker = { url: point.icon ? imgFolder + point.icon : "", anchor: new google.maps.Point(16, 16) }; var labelOffset = new google.maps.Size(-20, -35); var labelColor = "yellow"; if (!isLegacyIcons) { var sizes = getNewIconsSize(point.icon); labelOffset = sizes.labelOffset; labelColor = sizes.labelColor; imageMarker.anchor= sizes.imageMarkerAnchor; } var marker = new google.maps.Marker({ position: new google.maps.LatLng(lat, lon), icon: imageMarker }); //only add info bubble for actual units (with speed), not for landmarks or geos if (/\d{1,3}[mk]ph/.test(point.speed)) google.maps.event.addListener(marker, 'click', function(e) { openBubble(marker, point.id, point.description, point.dataset, point); }); if(imageMarker.url !== "") marker.setMap(map); var content = getLabelContent(labelColor, point.id); var myOptions = { content: content, boxStyle: { textAlign: "center", fontSize: "8pt" }, disableAutoPan: true, pixelOffset: labelOffset, position: new google.maps.LatLng(lat, lon), closeBoxURL: "", isHidden: false, zIndex: google.maps.Marker.MAX_ZINDEX + 1, pane: "floatShadow" }; var label = new InfoBox(myOptions); label.open(map); var toReturn = { marker: marker, dataset : point.dataset, label: label, id : point.id, description : point.description, point : point, //investigate for potential leaks hide: function(){ if (this.id == infoWindowModel.get('id')) infoBubble.close(); this.marker.setMap(null); this.label.setMap(null); } }; return toReturn; } //to be used with polygons and polylines and markers (calls hide() on removed item) function removeItem(collection, itemId){ var item = _.findWhere(collection, {id:itemId}); if (item){ item.hide(); collection.splice(collection.indexOf(item),1); console.log('Removed item with id = '+item.id); } } //removes all items from collection safely (calls removeItem on each item) function removeAllItems(collection){ var ids = _.pluck(collection, 'id'); // console.log(ids); ids.forEach(function(id){ removeItem(collection, id); }); } /* Removes all markers */ SfmbMapsGoogle.prototype.removeAllPoints = function() { removeAllItems(markers); }; SfmbMapsGoogle.prototype.removePoint = function(point) { removeItem(markers,point.id); }; /* Remove points from map @params points: [{id, latitude, longitude, color, description}...] */ SfmbMapsGoogle.prototype.removePoints = function(points) { points.forEach(function(e) { this.removePoint(e); }, this); }; SfmbMapsGoogle.prototype.showLabels = function() { showHideLabels(true); }; SfmbMapsGoogle.prototype.hideLabels = function() { showHideLabels(false); } function showHideLabels(show, points) { var theMap = show? map : null; if (points) { points.forEach(function(e, i, a) { markers.forEach(function(elem, i, a){ if (elem.id == e.id) elem.label.setMap(theMap); }); }); } else { markers.forEach(function(e, i, a) { e.label.setMap(theMap); }); } } /* @params points: [{id, latitude, longitude, color, description}...] */ SfmbMapsGoogle.prototype.putLabels = function(points) { showHideLabels(true, points); } /* @params points: [{id, latitude, longitude, color, description}...] */ SfmbMapsGoogle.prototype.removeLabels = function(points) { showHideLabels(false, points); } /* @params: lat: latitude - double lon: longitude - double zoom: int */ SfmbMapsGoogle.prototype.centerZoom = function(lat, lon, zoom) { map.panTo(new google.maps.LatLng(lat, lon)); if (zoom > 0) map.setZoom(parseInt(zoom)); } /* @params mapType: string */ SfmbMapsGoogle.prototype.setMapType = function(mapType) { map.setMapTypeId(getMapTypeFromString(mapType)); } function getMapTypeFromString(mapType) { if (mapType == "map") { return google.maps.MapTypeId.ROADMAP; } else if (mapType == "satellite") { return google.maps.MapTypeId.SATELLITE; } else if (mapType == "hybrid") { return google.maps.MapTypeId.HYBRID; } else if (mapType == "terrain") { return google.maps.MapTypeId.TERRAIN; } } /* @params */ SfmbMapsGoogle.prototype.getMapType = function() { result = ""; var mapType = map.getMapTypeId(); if (mapType == google.maps.MapTypeId.ROADMAP) { result = "map"; } else if (mapType == google.maps.MapTypeId.SATELLITE) { result = "satellite"; } else if (mapType == google.maps.MapTypeId.HYBRID) { result = "hybrid"; } else if (mapType == google.maps.MapTypeId.TERRAIN) { result = "terrain"; } return result; } /* @params address: string */ SfmbMapsGoogle.prototype.getLocation = function(address) { // not implemented } /* @params lat: double lon: double */ SfmbMapsGoogle.prototype.getAddress = function(lat, lon) { // not implemented } /* @params markerName: string */ SfmbMapsGoogle.prototype.openInfoBubble = function(markerName) { var marker = _.findWhere(markers, {id:markerName}); if (marker) openBubble(marker.marker, marker.id,marker.description, marker.dataset, marker.point); }; function openBubble(marker, name, description, dataset, point) { var streetViewService = new google.maps.StreetViewService(); streetViewService.getPanoramaByLocation(marker.getPosition(), 100, function(streetViewPanoramaData, status){ var svavailable = (status === google.maps.StreetViewStatus.OK); // infoBubble.content = getBubbleContent(name, description, dataset, svavailable, point); point.hasStreetView = svavailable; // console.dir(point); //update info window only if the info point is different if (infoWindowModel.get('id') != point.id){ infoBubble.close(); infoWindowModel.mergePoint(point); } infoBubble.open(map, marker); // setTimeout(function(){infoBubble.content.style.display = 'block';},50); }); map.getStreetView().setPosition(marker.getPosition()); }; SfmbMapsGoogle.prototype.updatePolygon = function(id, propName, propValue){ var options = {}; var translate = { "borderColor" : "strokeColor", "borderOpacity" : "strokeOpacity", "borderWidth" : "strokeWeight", "color" : "fillColor", "opacity" : "fillOpacity" } options[translate[propName]] = propValue; var thePolygon = _(polygons).findWhere({id:id}); // console.log(thePolygon); // console.log(options); // thePolygon.polygon.setOptions({fillColor:'#aa0000'}); thePolygon && thePolygon.polygon.setOptions(options); }; /* Add a polygon to map @params name: string color: string points: [{latitude, longitude}...] */ SfmbMapsGoogle.prototype.setPolygon = function(name, color, points, opacity, borderColor, borderOpacity, borderWidth) { var isNew = false; addPolygonOnMap(name, color, points, isNew, opacity, borderColor, borderOpacity, borderWidth); }; function addPolygonOnMap(name, color, points, isNew, opacity, borderColor, borderOpacity, borderWidth) { console.log("borderColor = " + borderColor + " width = " + borderWidth); var polygonAttr = { name: name, color: color, opacity : opacity, borderColor : borderColor, borderWidth : borderWidth, borderOpacity : borderOpacity }; var coords = []; if (!isNew) { points.forEach(function(e, i, a) { coords.push(new google.maps.LatLng(e.latitude, e.longitude)); }); } var polygon = new google.maps.Polygon({ clickable: false, path: coords, strokeColor: borderColor, strokeOpacity: borderOpacity, strokeWeight: borderWidth, fillColor: color, fillOpacity: opacity }); if (isNew) { polygon.editable = true; } var toAdd = { polygonAttr: polygonAttr, polygon: polygon, id:polygonAttr.name, hide:function(){ google.maps.event.clearListeners(this.polygon.getPath(),"set_at"); google.maps.event.clearListeners(this.polygon.getPath(), "insert_at"); this.polygon.setMap(null); if (editingPolygon && editingPolygon.id == this.id) { editingPolygon = null; newOnly = false; map.setOptions({draggableCursor:'default'}); } } }; polygons.push(toAdd); polygon.setMap(map); if (isNew) return polygons[polygons.length - 1]; } /* name is optional. If name == null -> remove all @params name: string */ SfmbMapsGoogle.prototype.deletePolygon = function(name) { console.log('removing polygon '+name); if (!name) this.deleteAllPolygons(); else{ var p = _.findWhere(polygons, {polygonAttr : {name:name}}); if (p) p.polygon.setMap(null); removeItem(polygons,name); } map.setOptions({draggableCursor:'default'}); } SfmbMapsGoogle.prototype.deleteAllPolygons = function() { polygons.forEach(function(p) {p.polygon.setMap(null);}); console.log("------------------removing all polygons...."); removeAllItems(polygons); } /* Add polyline on map @params setAutozoom: bool color: string thickness: double between 0 and 1 points: [{latitude, longitude}...] name: poly name */ SfmbMapsGoogle.prototype.setPoly = function(setAutozoom, color, thickness, points, name) { var polyAttr = { name: name, color: color, thickness: thickness }; var coords = []; // console.log("Poly color = " + polyAttr.color); points.forEach(function(e, i, a) { coords.push(new google.maps.LatLng(e.latitude, e.longitude)); }); var poly = new google.maps.Polyline({ path: coords, strokeColor: color, strokeOpacity: thickness, strokeWeight: 4 }); polys.push({ polyAttr: polyAttr, poly: poly, id:polyAttr.name, hide:function(){ this.poly.setMap(null); } }); poly.setMap(map); } SfmbMapsGoogle.prototype.deletePoly = function(name) { if (name) { p = _.findWhere(polys,{polyAttr : {name:name}}); if (p) p.poly.setMap(null); removeItem(polys,name); } else { deleteAllPolys(); } } function deleteAllPolys() { polys.forEach(function(p){p.poly.setMap(null);}); removeAllItems(polys); } SfmbMapsGoogle.prototype.newPolygon = function(name, color, opacity, borderColor, borderOpacity, borderWidth) { console.log("opacity = " + opacity + " borderColor = " + borderColor); var isNew = true; editingPolygon = addPolygonOnMap(name, color, null, isNew, opacity, borderColor, borderOpacity, borderWidth); map.setOptions({draggableCursor:'crosshair'}); addHandlersToPolygon(editingPolygon.polygon); newOnly = true; } /* Get the polygon info name is optional. If name == null -> remove all @params name: string @return: */ SfmbMapsGoogle.prototype.getPolygon = function(name) { var polygonArray = []; if (name) { var thePolygon = _(polygons).findWhere({id:name}); if (thePolygon) { polygonArray = { name: thePolygon.polygonAttr.name, color: thePolygon.polygonAttr.color, points: getPolygonLatLongString( thePolygon.polygon)}; } } else { polygons.forEach(function(e, i, a) { polygonArray.push({ name: e.polygonAttr.name, color:e.polygonAttr.color, points:getPolygonLatLongString( e.polygon)}); }); } console.log(JSON.stringify(polygonArray)); if (getPolygonCallback) { getPolygonCallback(polygonArray); } return polygonArray; //[{name, color, [{latitude, longitude}...]}...] } function getPolygonLatLongString(polygon) { var pointsString = []; points = polygon.getPath(); var length = points.getLength(); for (var i = 0; i < length; i++) { var p = points.getAt(i); pointsString.push({ latitude: p.lat(), longitude: p.lng() }); } return pointsString; } /* @params: name: string */ SfmbMapsGoogle.prototype.startEditPolygon = function(name) { var polygon = _(polygons).findWhere({id:name}); if (polygon) { polygon.polygon.setEditable(true); editingPolygon = polygon; map.setOptions({draggableCursor:'crosshair'}); addHandlersToPolygon(polygon.polygon); } } function addHandlersToPolygon(p) { google.maps.event.addListener(p.getPath(), 'set_at', function(index) { if (polyPointCallback) { var somePoints = getPolygonLatLongString(p); console.log(JSON.stringify(somePoints)); polyPointCallback(somePoints); } }); google.maps.event.addListener(p.getPath(), 'insert_at', function(index) { if (polyPointCallback) { var somePoints = getPolygonLatLongString(p); console.log(JSON.stringify(somePoints)); polyPointCallback(somePoints); } }); } SfmbMapsGoogle.prototype.endEditPolygon = function() { google.maps.event.clearListeners(editingPolygon.polygon.getPath(), "set_at"); google.maps.event.clearListeners(editingPolygon.polygon.getPath(), "insert_at"); editingPolygon.polygon.setEditable(false); editingPolygon = null; newOnly = false; map.setOptions({draggableCursor:'default'}); } /* Clear the items from map @params markers: [{id, latitude, longitude, color, description}...] polygons: [[name, color, {latitude, longitude}...]...] polys: [[{latitude, longitude}...]...] */ SfmbMapsGoogle.prototype.clearFromMap = function(markers, polygons, polys) { // NOT USED } /* Clear all from map */ SfmbMapsGoogle.prototype.resetAll = function() { infoBubble.close(); this.removeAllPoints(); this.deletePolygon(); this.deletePoly(); } /* @params name: string color: string */ SfmbMapsGoogle.prototype.changePolygonColor = function(name, color) { var polygon = _(polygons).findWhere({id:name}); if (polygon) { polygon.polygonAttr.color = color; polygon.polygon.setOptions({ fillColor:color, strokeColor: color}); } } /* Geocoding */ SfmbMapsGoogle.prototype.geocode = function(address){ var geocoder = new google.maps.Geocoder(); var self = this; geocoder.geocode( { 'address': address}, function(results, status) { if (status == google.maps.GeocoderStatus.OK) { var place = { lat: results[0].geometry.location.lat(), lon: results[0].geometry.location.lng(), display_name: results[0].formatted_address } self.openSimplePopup(place); } }); } var markerGeocode = null; SfmbMapsGoogle.prototype.openSimplePopup = function(place){ var path = "http://chart.apis.google.com/chart?chst=d_bubble_icon_text_small&chld=flag|bbtl|"+place.display_name+"|FFFFaa|000000"; var shadowPath = "http://chart.apis.google.com/chart?chst=d_bubble_icon_text_small_shadow&chld=flag|bbtl|"+place.display_name+"|FFFF88|000000"; map.panTo(new google.maps.LatLng(place.lat, place.lon)); var iconPath = { anchor: new google.maps.Point(0, 0), url: path }; var iconShadowPath = { anchor: new google.maps.Point(0, 0), url: shadowPath }; var markerGeocode = new google.maps.Marker({ map: map, position: new google.maps.LatLng(place.lat, place.lon), icon: iconPath, shadow: iconShadowPath, anchorPoint: new google.maps.Point(0, 0), }); google.maps.event.addListener(markerGeocode, 'click', function(event) { markerGeocode.setMap(null); markerGeocode = null; }); } var traffic = new google.maps.TrafficLayer(); SfmbMapsGoogle.prototype.setTraffic = function(bool) { if (bool) { traffic.setMap(map); } else { traffic.setMap(null); } } SfmbMapsGoogle.prototype.setMapKind = function() { // TODO NOT USED } SfmbMapsGoogle.prototype.getMapKind = function() { return "Google"; } SfmbMapsGoogle.prototype.setLegacyIcons = function(legacyIcons){ console.log("Legacy icons for Google Maps are: "+legacyIcons); isLegacyIcons = JSON.parse(legacyIcons); } } var SfmbMaps = SfmbMapsGoogle;