/** * Map control */ Ext.define('Common.control.MapView', { extend: 'Ext.panel.Panel', alias: 'widget.UNOPS.mapview', config: { mapPanel: null, controls: null, markerTemplate: null, map: null, itemsArray: [], markerArray: [], mapItemCollection: [], zoomLevel: 1, includeAutoZoom: 'marker', minAutoZoomLevel: null, maxAutoZoomLevel: null, minZoom: 1, maxZoom: 15, itemId: 'mapControl', popupWindowType: 'default', popupWindowMaxWidth: null, popupWindowMaxHeight: null, showCluster: true, autoRenderMap: false }, itemId: 'mapPanel', cls: 'googleMap', layout: { type: 'fit', align: 'stretch' }, /** * Initialize the control */ initComponent: function () { var me = this; me.mapPanel = me; me.map = null; me.addListener('afterlayout', function (mapControl) { if (me.autoRenderMap) { me._loadStore.call(me); } }); me.addListener('beforedestroy', function (context, options) { // This is needed because Google maps is not an ExtJS component // We add it to the idle event to help revent errors when Google callsback // and the map is already gone google.maps.event.addListener(me.mapPanel.map, 'idle', function () { me.map = null; }); return true; }); // After this all of Ext properties and object functions are available me.callParent(); // Defaults me.itemsArray = new Array(); me.markerArray = new Array(); me.mapItemCollection = new Array(); if (Ext.isEmpty(me.mapHeight)) { me.mapHeight = me.height; } // Apply default values to the controls Ext.applyIf(me.controls, { panControl: false, zoomControl: false, mapTypeControl: false, scaleControl: false, streetViewControl: false, overviewMapControl: false, mapTypeId: google.maps.MapTypeId.TERRAIN }); }, /** * Refresh the content. */ refreshContent: function () { var me = this; me._loadStore(); }, /** * Load the data from the map store if there is a store available */ _loadStore: function () { var me = this; if (me.rendered && me.isVisible()) { me.fastCreateMap(); var mask = new Ext.LoadMask(me.mapPanel.getEl(), {msg:"Loading..."}); mask.show(); if (!Ext.isEmpty(me.store)) { me.store.load({ scope: me, callback: function (records, operation, success) { me._removeMarkers(); me.createMap(); mask.destroy(); } }); } } else { // Defer until map is visible window.setTimeout(function() { me._loadStore.call(me); }, 1000); } }, /** * Remove all the map markers */ _removeMarkers: function () { if (this.markerBuffer != null) { for (var i = 0, len = this.markerBuffer.length; i < len; i++) { this.markerBuffer[i].setMap(null); this.markerBuffer[i] = null; } } if (this.markerArray != null) { for (var i = 0, len = this.markerArray.length; i < len; i++) { this.markerArray[i].setMap(null); this.markerArray[i] = null; } } if (this.itemsArray != null) { for (var i = 0, len = this.itemsArray.length; i < len; i++) { this.itemsArray[i] = null; } } if (this.markerClusters != null) { for (var i = 0; i < this.markerClusters.length; i++) { this.markerClusters[i].clearMarkers(); this.markerClusters[i].repaint(); this.markerClusters[i] = null; } } this.markerBuffer = new Array(); this.markerClusters = new Array(); ; this.itemsArray = new Array(); this.markerArray = new Array(); }, fastCreateMap: function () { var me = this; me.itemsArray = new Array(); me.markerArray = new Array(); // TAD: OSM Support /* TAD: From http://wiki.openstreetmap.org/wiki/Google_Maps_Example Build list of map types. You can also use var mapTypeIds = ["roadmap", "satellite", "hybrid", "terrain", "OSM"] but static lists work poorly when google updates the default list of map types. */ var mapTypeIds = []; for (var type in google.maps.MapTypeId) { mapTypeIds.push(google.maps.MapTypeId[type]); } mapTypeIds.push("OSM"); var myOptions = { center: new google.maps.LatLng(0, 0), // Default center (map won't draw without this!) zoom: 1, // Default zoom (map won't draw without this!) mapTypeId: me.mapTypeId, scrollwheel: false, mapTypeControl: true, maxZoom: me.maxZoom, minZoom: me.minZoom, mapTypeControlOptions: { mapTypeIds: mapTypeIds } }; Ext.applyIf(myOptions, me.controls); Ext.applyIf(myOptions, me.options); // Create a map on this control's dom element me.mapPanel.map = null; // Clear the existing map (if any) me.mapPanel.map = new google.maps.Map(me.mapPanel.getEl().dom, myOptions); var styles = [ { "featureType": "water", "elementType": "geometry", "stylers": [ { "color": "#6a9dc8" } ] }, { "featureType": "water", "elementType": "labels.text.fill", "stylers": [ { "visibility": "on" }, { "color": "#0b3a51" } ] }, { "featureType": "water", "elementType": "labels.text.stroke", "stylers": [ { "visibility": "off" } ] }, { "featureType": "poi.park", "elementType": "geometry.fill", "stylers": [ { "color": "#dbe1c6" } ] }, { "featureType": "poi.attraction", "elementType": "geometry.fill", "stylers": [ { "color": "#acba97" } ] }, { "featureType": "landscape.man_made", "elementType": "geometry", "stylers": [ { "color": "#f0f0f0" } ] }, { "featureType": "administrative", "elementType": "labels.text.fill", "stylers": [ { "color": "#555555" } ] }, { "featureType": "transit", "stylers": [ { "visibility": "simplified" } ] }, { "featureType": "administrative", "elementType": "labels.text" } ] ; me.mapPanel.map.setOptions({ styles: styles }); me.mapPanel.map.workspaceMapControl = me; // TAD: OSM Support this.mapPanel.map.mapTypes.set("OSM", new google.maps.ImageMapType({ getTileUrl: function (coord, zoom) { return "http://tile.openstreetmap.org/" + zoom + "/" + coord.x + "/" + coord.y + ".png"; }, tileSize: new google.maps.Size(256, 256), name: "OSM", maxZoom: 18 })); this.addMapControls(); }, /** * Create the map */ createMap: function () { var me = this; me.markerClusters = new Array(); // If clustering if (me.showCluster) { //if not multi clustering if (Ext.isEmpty(me.clusterFilterArray) || me.clusterFilterArray.length < 1) { me.addItemArray(me.store.data.items); me.markerCluster = new MarkerClusterer(me.mapPanel.map, me.markerBuffer, { imagePath: 'https://data.unops.org/Resources/images/m' }); google.maps.event.addListenerOnce(this.markerCluster, 'clusteringend', function () { me.autoZoom(); me.markerCluster.repaint(); }); } else { this.markerBuffer = new Array(); for (var i = 0; i < me.clusterFilterArray.length; i++) { me.store.filterBy(function (item) { return item.get(me.clusterFilterField) == me.clusterFilterArray[i]; }); me.addItemArray(me.store.data.items); me.markerClusters.push(new MarkerClusterer(me.mapPanel.map, me.markerBuffer, { imagePath: '/Apps/Workspace/Resources/images/' + me.clusterFilterArray[i] })); google.maps.event.addListenerOnce(me.markerClusters[me.markerClusters.length - 1], 'clusteringend', function (markerCluster) { markerCluster.repaint(); }); } me.autoZoom(); } } else if (this.store.count() > 0) { me.addItemArray(me.store.data.items); me.autoZoom(); } /* var passedKML = Common.getParamFromQueryString("KML", ""); var passedFusion = Common.getParamFromQueryString("Fusion", ""); if (!Ext.isEmpty(passedKML)) { var kmlLayer = new google.maps.KmlLayer(passedKML); kmlLayer.setMap(this.mapPanel.map); } if (!Ext.isEmpty(passedFusion)) { var passedCountry = Common.getParamFromQueryString("Country", ""); var fusionLayer = new google.maps.FusionTablesLayer(passedFusion, { suppressInfoWindows: false }); fusionLayer.setMap(this.mapPanel.map); fusionLayer.setQuery("select geometry from " + passedFusion + " where description like '%" + passedCountry + "%'"); } */ }, autoZoom: function () { if (Ext.isDefined(this.AutoZoomLevel)) { var mapCenter = this.itemsArray[0]; //new google.maps.LatLng(avgLat, avgLng); this.mapPanel.map.setCenter(mapCenter); this.mapPanel.map.setZoom(this.AutoZoomLevel); return; } //If we've no items we cannot auto zoom. if (this.itemsArray.length < 1 || Ext.isEmpty(this.mapPanel.map)) { return; } var latlng = this.itemsArray; var latlngbounds = new google.maps.LatLngBounds(); for (var i = 0; i < latlng.length; i++) { latlngbounds.extend(latlng[i]); } var tooBig = 190; var tooSmall = 100; var tooBigSizer = 30; var tooSmallSizer = 2; try { var spreadB = Math.abs(latlngbounds.fa.b - latlngbounds.ga.b); var spreadJ = Math.abs(latlngbounds.fa.d - latlngbounds.ga.d); var spreadRatio = spreadB / spreadJ; var spread = Math.max(spreadB, spreadJ); } catch (err) { //return; } var maxLat = -1000; var minLat = 1000; var maxLng = -1000; var minLng = 1000; var avgLng = 0; var avgLat = 0; for (var counter = 0; counter < latlng.length; counter++) { maxLat = Math.max(maxLat, latlng[counter].lat()); minLat = Math.min(minLat, latlng[counter].lat()); avgLat = avgLat + latlng[counter].lat(); maxLng = Math.max(maxLng, latlng[counter].lng()); minLng = Math.min(minLng, latlng[counter].lng()); avgLng = avgLng + latlng[counter].lng(); } avgLat = avgLat / latlng.length; avgLng = avgLng / latlng.length; var mapCenter = new google.maps.LatLng(avgLat, avgLng); var latSpread = Math.abs(maxLat - minLat); var lngSpread = Math.abs(maxLng - minLng); var altSpread = Math.max(latSpread, lngSpread); this.mapPanel.map.setCenter(mapCenter); this.mapPanel.map.fitBounds(latlngbounds); if (this.mapPanel.map.getZoom() > 10) this.mapPanel.map.setZoom(10); }, fillInAdditionalParams: function (location) { Ext.Object.each(this.extraParams, function (key, value, myself) { key = key.substr(1); location.data[key] = value; }); }, addItemArray: function (itemArrayCollection) { var me = this; me.markerBuffer = new Array(); if (Ext.isArray(itemArrayCollection)) { for (var i = 0; i < itemArrayCollection.length; i++) { var tempPreItem = itemArrayCollection[i]; var tempItem = tempPreItem.data; if (tempItem.PointType == 'marker') { if (Ext.isDefined(tempItem.items)) { me.addMarkers(tempItem.items); } else { me.addMarker(tempItem); } } else if (tempItem.PointType == 'shape') { me.addShapes(tempItem.items); } else if (tempItem.PointType == 'line') { me.addLines(tempItem.items); } } } }, addLines: function (lines) { if (Ext.isArray(lines)) { for (var i = 0; i < lines.length; i++) { var tempLine; tempLine = new google.maps.Polyline(lines[i]); tempLine.setMap(this.mapPanel.map); if (this.includeAutoZoom == 'all') { if (Ext.isArray(lines[i].path)) { var path = lines[i].path; for (var j = 0; j < path.length; j++) { this.itemsArray.push(path[j]); } } } } } }, addShapes: function (shapes) { if (Ext.isArray(shapes)) { for (var i = 0; i < shapes.length; i++) { var tempShape; tempShape = new google.maps.Polygon(shapes[i]); tempShape.setMap(this.mapPanel.map); if (this.includeAutoZoom == 'all') { if (Ext.isArray(shapes[i].paths)) { var paths = shapes[i].paths; for (var j = 0; j < paths.length; j++) { this.itemsArray.push(paths[j]); } } } } } }, addMarkers: function (markers) { if (Ext.isArray(markers)) { for (var i = 0; i < markers.length; i++) { var tempMarker; tempMarker = new google.maps.Marker(markers[i]); tempMarker = this.useCommonIcons(tempMarker); this.itemsArray.push(tempMarker.position); this.markerArray.push(tempMarker); } this.markerCluster = new MarkerClusterer(this.mapPanel.map, this.markerArray, { imagePath: Common.getResourcesPath() + 'images/m' }); } }, addMarker: function (marker, index) { var me = this; var map = this.mapPanel.map; if (marker.lat != null && marker.lng != null) { var tempMarker; var markerClone; markerClone = Ext.clone(marker); markerClone.position = new google.maps.LatLng(markerClone.lat, markerClone.lng); if (!this.showCluster) { // Clustering is off, assign the map to the marker markerClone.map = map; } tempMarker = new google.maps.Marker(markerClone); tempMarker.template = this.markerTemplate; google.maps.event.addListener(tempMarker, 'click', function () { if (tempMarker.template == null) { tempMarker.template = '
\ {lat},{lng}\
'; } var tpl = new Ext.XTemplate(tempMarker.template); if (me.popupWindowType == 'google') { var infoWindow = new google.maps.InfoWindow({ content: tpl.applyTemplate(tempMarker) }); infoWindow.open(tempMarker.map, tempMarker); } else if (me.popupWindowType == 'default') { if (this.popupWindowMaxWidth == null) { this.popupWindowMaxWidth = Math.round(map.workspaceMapControl.getWidth() / 2); } if (this.popupWindowMaxHeight == null) { this.popupWindowMaxHeight = Math.round(map.workspaceMapControl.getHeight() / 2); } contentString = '
' + tpl.applyTemplate(tempMarker) + '
'; var infoBubble = new InfoBubble({ content: contentString, disableAnimation: true, maxWidth: this.popupWindowMaxWidth, maxHeight: this.popupWindowMaxHeight, borderColor: '#666666', borderRadius: '0px' }); infoBubble.open(map, tempMarker); google.maps.event.addListener(tempMarker, 'click', function () { if (!infoBubble.isOpen()) { infoBubble.open(map, tempMarker); } }); } }); tempMarker = this.useCommonIcons(tempMarker); if (Ext.isEmpty(index) || index < 0) { this.itemsArray.push(tempMarker.position); this.markerArray.push(tempMarker); this.markerBuffer.push(tempMarker); } else { this.itemsArray[index] = tempMarker.position; this.markerArray[index] = tempMarker; this.markerBuffer.push(tempMarker); } } }, useCommonIcons: function (marker) { switch (marker.icon) { case 'GLOBE': marker.icon = 'http://www.dineright.com/Images/httpbutton.png'; break; case 'COMPASS': marker.icon = 'http://www.sharepointusergroup.net.nz/Shared/map_icon.gif'; break; default: if (!Ext.isEmpty(marker.icon)) { marker.icon = Common.getContentImagePath() + marker.icon + '.png'; } } return marker; }, addMapControls: function () { if (this.gmapType === 'map') { if (Ext.isArray(this.mapControls)) { for (var i = 0; i < this.mapControls.length; i++) { this.addMapControl(this.mapControls[i]); } } else if (typeof this.mapControls === 'string') { this.addMapControl(this.mapControls); } else if (typeof this.mapControls === 'object') { this.map.addControl(this.mapControls); } } }, addMapControl: function (mc) { var mcf = window[mc]; if (typeof mcf === 'function') { this.getMap().addControl(new mcf()); } }, addOptions: function () { if (Ext.isArray(this.mapConfOpts)) { for (var i = 0; i < this.mapConfOpts.length; i++) { this.addOption(this.mapConfOpts[i]); } } else if (typeof this.mapConfOpts === 'string') { this.addOption(this.mapConfOpts); } }, addOption: function (mc) { var mcf = this.getMap()[mc]; if (typeof mcf === 'function') { map[mc](); } } });