/** * Gmap ArcGIS wrapper. */ function gmapArcgis(config) { /** * Extend Google Maps API. * See https://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; } } // Proxy settings if (config.proxyUrl != undefined) { gmaps.ags.Config.proxyUrl = config.proxyUrl; if (config.alwaysUseProxy != undefined) { gmaps.ags.Config.alwaysUseProxy = config.alwaysUseProxy; } } // Map initialization if (config.mapId != null && config.mapOptions != null) { config.map = new google.maps.Map(document.getElementById(config.mapId), config.mapOptions); } // Return a Gmap ArcGIS object return { // Storable properties config: config, map: (config.map != undefined) ? config.map : null, // UI unblocker wrapper unblockUI: function(name) { if (window.blockUI == undefined) { window.blockUI = [ ]; } if (name == undefined) { name = 'default'; } if (window.blockUI[name] != undefined && window.blockUI[name] == true) { delete window.blockUI[name]; } if (window.blockUI.length == 0) { if (this.config.overlaySelector != undefined) { $(this.config.overlaySelector).unblock(); } else { jQuery.unblockUI(); } } }, // UI blocker wrapper blockUI: function(params) { if (window.blockUI == undefined) { window.blockUI = [ ]; } if (params.name == undefined) { params.name = 'default'; } if (window.blockUI[params.name] == undefined || window.blockUI[params.name] != true) { window.blockUI[params.name] = true; if (this.config.overlaySelector != undefined) { $(this.config.overlaySelector).block(params); } else { jQuery.blockUI(params); } } }, // Block the UI and show an overlay showOverlay: function(name, timeout) { if (this.config.disableOverlay != undefined && this.config.disableOverlay == true) { return; } if ($.browser.msie === false || $.browser.msie === undefined) { message = this.config.overlayMessage; if (this.config.overlayCss != undefined) { css = this.config.overlayCss; } else { css = { }; } var params = { name: name, message: message, css: css, }; if (timeout != false) { params.timeout = timeout; } this.blockUI(params); } }, // Show overlay dependending on element configuration showOverlayIf: function(section, element) { if (this.config[section][element].overlayTime != undefined && this.config[section][element].overlayTime != false) { this.showOverlay(section + '_' + element, this.config[section][element].overlayTime); } else if (this.config[section][element].overlayTime != false) { this.showOverlay(section + '_' + element); } }, // Count object properties countProperties: function(object) { // Doesn't work with IE8 //return Object.keys(layers).length; var count = 0; for (value in object) { count++; } return count; }, // Set the current map setMap: function(map) { if (map != undefined) { this.map = map; this.setCenter(map.getCenter()); } }, // Get the default opacity of a layer element defaultOpacity: function(element, store) { store = (store != undefined) ? store : 'layers'; if (this.config[store][element] != undefined && this.config[store][element].opacity != undefined) { return this.config[store][element].opacity; } else { return 0.55; } }, // Add polygons from a given ArcGIS MapServer Layer showPolygon: function(params, name) { var self = this; params.service = new gmaps.ags.MapService(params['uri']); params.overlay = new gmaps.ags.MapOverlay(params.service, { exportOptions: { layerIds: params['layers'], layerOption: 'show', layerDefinitions: (params['layerDefinitions'] != undefined) ? params['layerDefinitions'] : { }, }, }); if (params.callback == undefined) { // Default callback: remove the blockUI once the layer is shown google.maps.event.addListener(params.overlay, 'drawend', function() { self.unblockUI(name); }); } else { google.maps.event.addListener(params.overlay, 'drawend', function() { params.callback(this.map, params); self.unblockUI(name); }); } params.overlay.setMap(this.map); params.overlay.setOpacity(params.opacity); this.identifyPolygon(params); return params.overlay; }, // Add an identify callback to a layer identifyPolygon: function(params) { // See https://google-maps-utility-library-v3.googlecode.com/svn/trunk/arcgislink/examples/identify.js if (typeof params.identify != 'undefined') { var self = this; // Configure event listener params.identifyListener = google.maps.event.addListener(this.map, 'click', function(point) { params.identify.geometry = point.latLng; params.identify.bounds = self.map.getBounds(); params.identify.width = self.map.getDiv().offsetWidth; params.identify.height = self.map.getDiv().offsetHeight; // Configure infoWindow if (params.infowindow != undefined) { params.infowindow.close(); params.infowindow.setContent(''); params.infowindow.setPosition(point.latLng); } else { params.infowindow = new google.maps.InfoWindow({ content: '', position: point.latLng, }); } // Identify layers params.service.identify(params.identify, function(identify, err) { if (!err) { var layers = { }; var result = null; var layerId = null; // Sort results by layer for (var i in identify.results) { result = identify.results[i]; layerId = result.layerId; if (layers[layerId] == undefined) { layers[layerId] = [ result ]; } else { layers[layerId].push(result); } } // Callback only if layers is not empty if (self.countProperties(layers) > 0) { params.identify.callback(layers, params.infowindow, err); } } }); }); } }, // Add a KML to the map showKML: function(element) { if (typeof this.config['kmls'][element].overlay == 'undefined') { this.config['kmls'][element].overlay = new google.maps.KmlLayer(this.config['kmls'][element].uri); } this.config['kmls'][element].overlay.setMap(this.map); this.config['kmls'][element].isVisible = true; this.unblockUI('kmls_' + element); }, // Add a KML layer into the map addKML: function(element) { if (typeof this.config['kmls'][element].overlay != 'undefined') { this.showKML(element); return; } this.showOverlayIf('kmls', element); this.showKML(element); }, // Add a layer to the map addLayers: function(element) { this.config['layers'][element].opacity = this.defaultOpacity(element); if (typeof this.config['layers'][element].overlay != 'undefined') { if (this.config['layers'][element].overlay.setVisible != undefined) { this.config['layers'][element].overlay.setVisible(true); this.identifyPolygon(this.config['layers'][element]); } else if (this.config['layers'][element].overlay.setOpacity != undefined) { this.config['layers'][element].overlay.setOpacity(this.config['layers'][element].opacity); this.identifyPolygon(this.config['layers'][element]); } else if (this.config['layers'][element].overlay[0] != undefined) { for (i=0; i < this.config['layers'][element].overlay.length; i++) { this.config['layers'][element].overlay[i].setVisible(true); } } this.unblockUI('layers_' + element); return; } this.showOverlayIf('layers', element); this.config['layers'][element].overlay = this.showPolygon(this.config['layers'][element], 'layers_' + element); }, // Add a slider layer in the map addSlider: function(layer, slider) { this.config['sliders'][slider].opacity = this.defaultOpacity(layer, 'sliders'); this.config['sliders'][slider].layers = [layer]; if (typeof this.config['sliders'][slider].overlays[layer] != 'undefined') { this.config['sliders'][slider].overlays[layer].setOpacity(this.config['sliders'][slider].opacity); this.unblockUI('sliders_' + slider); } this.showOverlayIf('sliders', slider); this.config['sliders'][slider].overlays[layer] = this.showPolygon(this.config['sliders'][slider], 'sliders_' + slider); this.sliderUpdateOpacity(layer, slider); }, // Add makers to the map addMarkers: function(element, cluster) { var self = this; if (cluster == undefined) { cluster = false; } if (this.config['markers'][element].overlay != undefined) { this.toggleMarkers(element, false, cluster); return; } else { this.config['markers'][element].overlay = []; self.config['markers'][element].listener = []; } this.showOverlayIf('markers', element); // Query parameters var query = { returnGeometry: true, geometry: (this.config['markers'][element].bounds != undefined) ? this.config['markers'][element].bounds : undefined, where: (this.config['markers'][element].where != undefined) ? this.config['markers'][element].where : '1=1', outFields: this.config['markers'][element].fields, }; var layer = new gmaps.ags.Layer(this.config['markers'][element].uri); // Configure infoWindow this.config['markers'][element].infowindow = new google.maps.InfoWindow({ content: '', }); // Execute query if (this.config['markers'][element].resultSetCallback != undefined) { layer.query(query, this.config['markers'][element].resultSetCallback); } else { layer.query(query, processResultSet); } // Process results function processResultSet(fset) { var fs = fset.features; var title; for (var i = 0, c = fs.length; i < c; i++) { var feature = fs[i]; if (self.config['markers'][element].title != undefined) { title = feature.attributes[self.config['markers'][element].title]; } else { title = feature.attributes.nome; } self.config['markers'][element].overlay[i] = {}; self.config['markers'][element].overlay[i] = new google.maps.Marker({ title: title, icon: self.config['markers'][element].icon, position: feature.geometry[0].getPosition(), map: self.map, }); // Use a closure so marker data remains available to the listeners self.config['markers'][element].listener[i] = function() { var marker = self.config['markers'][element].overlay[i]; var infowindow = self.config['markers'][element].infowindow; var content = self.config['markers'][element].content; var attributes = feature.attributes; google.maps.event.addListener(marker, 'click', function() { self.closeInfoWindows(); infowindow.setContent(content(attributes)); infowindow.open(self.map, marker); }); }; // Execute self.config['markers'][element].listener[i](); } if (cluster == true) { var maxZoom = (self.config['markers'][element].markersMaxZoom != undefined) ? self.config['markers'][element].markersMaxZoom : null; self.config['markers'][element].cluster = new MarkerClusterer(self.map, self.config['markers'][element].overlay, { ignoreHidden: true, maxZoom: maxZoom, }); } self.unblockUI('markers_' + element); } }, // Toggle KML visibility toggleKML: function(element) { if (this.config['kmls'][element].overlay == undefined) { return; } else if (this.config['kmls'][element].isVisible == true) { this.config['kmls'][element].isVisible = false; this.config['kmls'][element].overlay.setMap(); } else { this.config['kmls'][element].isVisible = true; this.config['kmls'][element].overlay.setMap(this.map); } }, // Toggle layer visibility toggleLayers: function(element) { if (this.config['layers'][element].overlay.setVisible != undefined) { var changeTo = !this.config['layers'][element].overlay.getVisible(); this.config['layers'][element].overlay.setVisible(changeTo); } else if (this.config['layers'][element].overlay.setOpacity != undefined) { var opacity = this.config['layers'][element].overlay.getOpacity; if (opacity == 0) { opacity = this.config['layers'][element].opacity; } else { opacity = 0; } this.config['layers'][element].overlay.setOpacity(opacity); } else if (jQuery.isArray(this.config['layers'][element].overlay)) { for (var i in this.config['layers'][element].overlay) { if (this.config['layers'][element].overlay[i].getVisible != undefined) { var changeTo = !this.config['layers'][element].overlay[i].getVisible(); this.config['layers'][element].overlay[i].setVisible(changeTo); } // Nested layers from showPolygonFromQueryMultiple if (jQuery.isArray(this.config['layers'][element].overlay[i])) { for (var j in this.config['layers'][element].overlay[i]) { if (this.config['layers'][element].overlay[i][j].getVisible != undefined) { var changeTo = !this.config['layers'][element].overlay[i][j].getVisible(); this.config['layers'][element].overlay[i][j].setVisible(changeTo); } } } } } if (this.config['layers'][element].identifyListener != undefined) { google.maps.event.removeListener(this.config['layers'][element].identifyListener); } }, // Toggle marker visibility toggleMarkers: function(element, raw, cluster) { var markers = (raw == true) ? element : this.config['markers'][element].overlay; var isVisible = false; // Hide/show each marker for (i=0; i < markers.length; i++) { if (markers[i].marker.getVisible()) { isVisible = true; markers[i].marker.setVisible(false); } else { markers[i].marker.setVisible(true); } } // Process clusters if (cluster == true) { this.config['markers'][element].cluster.repaint(); } // Close the infoWindow if (isVisible == true && this.config['markers'] != undefined && this.config['markers'][element] != undefined && this.config['markers'][element].infowindow != undefined) { this.config['markers'][element].infowindow.close(); } }, // Change a element in the map changeElement: function(type, element, callback) { if (this.config[type][element] == undefined || this.config[type][element]['showOn'] == undefined) { return; } if (jQuery.inArray(this.config.mapId, this.config[type][element]['showOn']) != -1) { this[callback](element); } }, // Display slider sliderUpdate: function(layer, slider) { if (this.config['sliders'][slider].overlays == undefined) { this.config['sliders'][slider].overlays = [ ]; } if (this.config['sliders'][slider].overlays[layer] == undefined) { this.addSlider(layer, slider); } this.sliderUpdateOpacity(layer, slider); }, // Fix slider opacity sliderUpdateOpacity: function(layer, slider) { for (var i in this.config['sliders'][slider].overlays) { if (i == layer) { this.config['sliders'][slider].overlays[i].setOpacity(this.defaultOpacity(slider, 'sliders')); } else { this.config['sliders'][slider].overlays[i].setOpacity(0); } } // The second and all subsequent layers shall be intialized without opacity. this.sliderOpacity = 0; }, // jQuery slider wrapper slider: function(element, slider) { var self = this; var type = (self.config.sliders[slider].type != undefined) ? self.config.sliders[slider].type : 'slider'; var value = (self.config.sliders[slider].value != undefined) ? self.config.sliders[slider].value : 1; var min = (self.config.sliders[slider].min != undefined) ? self.config.sliders[slider].min : 1; var max = (self.config.sliders[slider].max != undefined) ? self.config.sliders[slider].max : 1; var step = (self.config.sliders[slider].step != undefined) ? self.config.sliders[slider].step : 1; if (type == 'selectToUISlider') { $(element).selectToUISlider({ sliderOptions: { slide: function(event, ui) { self.sliderUpdate.apply(self, [ ui.value - 1, slider ]); } } }).hide(); } else { // Max is the number of layers in the service $(element).slider({ value: value, min: min, max: max, step: step, animate: true, change: function(event, ui) { self.sliderUpdate.apply(self, [ ui.value, slider ]); }, }); } }, // Show slider in the map showSlider: function(slider) { for (var i in this.config['sliders'][slider].overlays) { this.config['sliders'][slider].overlays[i].setMap(this.map); } }, // Hide slider from the map hideSlider: function(slider) { for (var i in this.config['sliders'][slider].overlays) { this.config['sliders'][slider].overlays[i].setMap(null); } }, /** * Returns the zoom level at which the given rectangular region fits in the map view. * The zoom level is computed for the currently selected map type. * * @param {google.maps.Map} map * @param {google.maps.LatLngBounds} bounds * @return {Number} zoom level * @see http://stackoverflow.com/questions/9837017/equivalent-of-getboundszoomlevel-in-gmaps-api-3 */ getZoomByBounds: function(bounds) { var map = this.map; var MAX_ZOOM = map.mapTypes.get(map.getMapTypeId()).maxZoom || 21; var MIN_ZOOM = map.mapTypes.get(map.getMapTypeId()).minZoom || 0; var ne = map.getProjection().fromLatLngToPoint(bounds.getNorthEast()); var sw = map.getProjection().fromLatLngToPoint(bounds.getSouthWest()); var worldCoordWidth = Math.abs(ne.x - sw.x); var worldCoordHeight = Math.abs(ne.y - sw.y); // Fit padding in pixels var FIT_PAD = 40; for (var zoom = MAX_ZOOM; zoom >= MIN_ZOOM; --zoom) { if (worldCoordWidth * (1 << zoom) + 2 * FIT_PAD < $(map.getDiv()).width() && worldCoordHeight * (1 << zoom) + 2 * FIT_PAD < $(map.getDiv()).height()) { return zoom; } } return 0; }, // Show a polygon resulting from the given query showPolygonFromQuery: function(params, name, count) { var self = this; var limit = null; var layer = new gmaps.ags.Layer(params.uri); var query = { returnGeometry: true, geometryType: 'esriGeometryPolygon', where: params.where, outFields: '*', overlayOptions: (params.overlayOptions != undefined) ? params.overlayOptions : null, }; layer.query(query, function(fset) { var fs = fset.features; params.overlay = []; for (i in fs) { var feature = fs[i]; params.overlay[i] = feature.geometry[0]; // Fit map to polygon bounds if (params.fitBounds != undefined && params.fitBounds == true) { var bounds = params.overlay[i].getBounds(); // Join bounds together if (limit === null) { limit = bounds; } else { limit.union(bounds); } } // Show polygon params.overlay[i].setMap(self.map); // Forward all click events to the map // Somehow a polygon returned from a query is blocking all map clicks google.maps.event.addListener(params.overlay[i], 'click', function(point) { google.maps.event.trigger(self.map, 'click', point); }); } // Fit map to limits if (params.fitBounds != undefined && params.fitBounds == true) { var zoom = self.getZoomByBounds(limit); var center = limit.getCenter(); self.map.setZoom(zoom); self.setCenter(center, true); } self.unblockUI(name); if (name != undefined) { if (count != undefined) { if (self.config['layers'][name] == undefined) { self.config['layers'][name] = { }; } if (self.config['layers'][name].overlay == undefined) { self.config['layers'][name].overlay = []; } self.config['layers'][name].overlay[count] = params.overlay; } else { self.config['layers'][name].overlay = params.overlay; } } else { return params.overlay; } }); }, // Show a polygon resulting from the given query in multiple layers showPolygonFromQueryMultiple: function (params, name, layers) { var self = this; var where = params.where; // Format where clause for ajax request where = where.replace(/=/g, '%3D'); where = where.replace(/ /g, '+'); // First find out in which layers there are results for (var n in layers) { var layer = layers[n]; var args = jQuery.extend(true, {}, params); var url = params.uri + '/' + layer + '/query?where=' + where + '&f=pjson'; // A closure here keeps the right layer to the async response (function(layer, args) { $.ajax({ type: 'get', url: url, dataType: 'jsonp', success: function (response) { if (response.features != undefined && response.features.length != '0') { args.uri += '/' + layer; self.showPolygonFromQuery(args, name, n); } } }); })(layer, args); } }, // Close all registered infoWindows closeInfoWindows: function() { overlays = [ 'layers', 'markers' ]; for (i in overlays) { var overlay = this.config[overlays[i]]; for (element in overlay) { if (overlay[element].infowindow != undefined) { this.config[overlays[i]][element].infowindow.close(); } } } }, // Remove all loaded overlays cleanOverlays: function() { var overlays = [ 'layers', 'markers' ]; for (i in overlays) { var overlay = this.config[overlays[i]]; for (element in overlay) { if (overlay[element].infowindow != undefined) { delete this.config[overlays[i]][element].infowindow; } if (overlay[element].overlay != undefined) { delete this.config[overlays[i]][element].overlay; } } } }, // Add panoramio layer addPanoramio: function() { if (this.config.panoramio.panoramioLayer == undefined) { var self = this; this.showOverlay('panoramio'); var photoDiv = document.createElement('div'); var photoWidgetOptions = this.config.panoramio.photoWidgetOptions; this.config.panoramio = { }; this.config.panoramio.photoWidget = new panoramio.PhotoWidget(photoDiv, null, photoWidgetOptions); this.config.panoramio.infoWindow = new google.maps.InfoWindow(); this.config.panoramio.panoramioLayer = new google.maps.panoramio.PanoramioLayer({ suppressInfoWindows: true }); google.maps.event.addListener(this.config.panoramio.panoramioLayer, 'click', function(e) { var photoRequestOptions = { ids: [{ 'photoId': e.featureDetails.photoId, 'userId': e.featureDetails.userId, }] }; self.closeInfoWindows(); self.config.panoramio.photoWidget.setRequest(photoRequestOptions); self.config.panoramio.photoWidget.setPosition(0); self.config.panoramio.infoWindow.setPosition(e.latLng); self.config.panoramio.infoWindow.open(self.map); self.config.panoramio.infoWindow.setContent(photoDiv); }); } // See https://groups.google.com/forum/#!msg/panoramio-api/oQUJzcI-NM8/ARGPKppY33YJ panoramio.events.listen(this.config.panoramio.photoWidget, panoramio.events.EventType.PHOTO_CLICKED, photoClicked); function photoClicked(event) { var photo=event.getPhoto(); if(photo){ MaximizeWindow(window.open(photo.getPhotoUrl())); // opens a new window } function MaximizeWindow(hWnd){ hWnd.moveTo(0,0); hWnd.resizeTo(screen.width, screen.height); } } this.unblockUI('panoramio'); }, // Set the current map center, optionally updating the map setCenter: function(latLng, update) { this.config.mapCenter = latLng; if (update != undefined && update == true) { this.updateCenter(); } }, // Update map center updateCenter: function() { this.map.setCenter(this.config.mapCenter); }, } };