..\..\..\..\src\Hud.Common\Hud.Gis.Web\scripts\Common\MapBuilder\MapBuilderController.js File
define([
'dojo/_base/declare',
'dojo/dom',
'dojo/dom-construct',
'dojo/dom-class',
'dojo/dom-style',
'dojo/json',
'dojo/Deferred',
'dojo/query',
'dojo/on',
'dojo/request/xhr',
'dojo/Evented',
'esri/layers/FeatureLayer',
"esri/layers/GraphicsLayer",
'esri/geometry/webMercatorUtils',
"Hud.Common/Viewport/EsriMapController",
'dojo/text!./MapBuilderView.html',
"xstyle/css!./MapBuilder.css"
],
function (
declare,
dom,
domConstruct,
domClass,
domStyle,
JSON,
Deferred,
query,
on,
xhr,
Evented,
FeatureLayer,
GraphicsLayer,
webMercatorUtils,
MapController,
MapBuilderView) {
return declare([Evented], {
//properties
options: null,
config: null,
map: null,
defaultExtent: null,
defaultGraphic: null,
defaultBasemap: null,
selectedThematicLayer: null,
selectedPropertyLayers: [],
useLocalStorage: false,
webMapCollection: [],
localStorageKey: '_x_CustomMap_x_',
/**
* Constructs a new MapBuilder class.
* A component for building and saving customizable maps.
*
* @class Common.MapBuilder
* @constructor
*/
constructor: function (options) {
this.options = options;
this.map = options.map;
var d = this.getConfig();
var self = this;
d.then(function (config) {
self.config = config;
self.initialize();
});
},
/**
* Initializes the UI and event handlers.
* @method initialize
*/
initialize: function () {
var self = this;
//determine storage type
self.useLocalStorage = self.localStorageAvailable();
var item = window.localStorage.getItem(self.localStorageKey);
if (item && item !== null) {
var _item = JSON.parse(item);
if (Array.isArray(_item)) {
self.webMapCollection = _item;
}
}
//inject UI template
domConstruct.place(MapBuilderView, self.options.containerId, 'only');
//build UI form from config
self.buildForm();
/* BIND EVENTS */
//basemap layer change
var bl = query('input[name="basemap-options"]');
bl.on('click', function (e) {
var type = e.target.value;
self.map.setBasemap(type);
});
//thematic layer change
var tl = query('input[name="thematic-options"]');
tl.on('click', function (e) {
self.addThematicLayer(e.target.value);
self.emit("layer-changed", { map: e.map });
});
//property layer change
var pl = query('input[name="property-options"]');
pl.on('click', function (e) {
if (e.target.checked === true) {
self.addPropertyLayer(e.target.value);
}
else {
self.removePropertyLayer(e.target.value);
}
self.emit("layer-changed", { map: e.map });
});
//create map check change
on(dom.byId('chkNewMap'), 'change', function (e) {
self.toggleForm(e.target.checked);
});
//save click
on(dom.byId('btnSaveCustomMap'), 'click', function (e) {
self.save();
});
//cancel click
on(dom.byId('btnCancelCustomMap'), 'click', function (e) {
document.getElementById('chkNewMap').checked = false;
self.toggleForm(false);
});
},
buildForm: function () {
//basemaps
var basemapHtml = '';
for (var i = 0, _l = this.config.basemapLayers.length; i < _l; i++) {
var basemap = this.config.basemapLayers[i];
basemapHtml +=
'<div class="col-md-6">' +
'<div class="radio map-options-item">' +
'<label>' +
'<input type="radio" name="basemap-options" id="basemap-radio-' + basemap.id + '" value="' + basemap.id + '">' +
basemap.name +
'</label>' +
'</div>' +
'</div>';
if (i === 0) this.defaultBasemap = basemap.id;
}
domConstruct.place(basemapHtml, "basemap-layer-container", "only");
//thematic layers
var thematicHtml = '';
for (var i = 0, _l = this.config.thematicLayers.length; i < _l; i++) {
var thematicLayer = this.config.thematicLayers[i];
thematicHtml +=
'<div class="col-md-6">' +
'<div class="radio map-options-item">' +
'<label>' +
'<input type="radio" name="thematic-options" id="thematic-radio-' + thematicLayer.id + '" value="' + thematicLayer.id + '">' +
thematicLayer.name +
'</label>' +
'</div>' +
'</div>';
}
domConstruct.place(thematicHtml, "thematic-layer-container", "only");
//operational layers
var layerHtml = '';
for (var i = 0, _l = this.config.propertyLayers.length; i < _l; i++) {
var layer = this.config.propertyLayers[i];
layerHtml +=
'<div class="col-md-6">' +
'<div class="checkbox map-options-item">' +
'<label>' +
'<input name="property-options" type="checkbox" value="' + layer.id + '">' +
layer.name +
'</label>' +
'</div>' +
'</div>';
}
domConstruct.place(layerHtml, "property-layer-container", "only");
//web maps table
var tableHtml =
'<table class="table table-striped table-condensed">' +
'<caption>Saved Maps</caption>' +
'<thead>' +
'<tr><th>#</th><th>Map Name</th><th></th><th></th></tr>' +
'</thead>' +
'<tbody id="custom-map-table"></tbody>' +
'</table>';
domConstruct.place(tableHtml, "div-custommaptable", "only");
this.updateMapTable();
//disable table buttons on initial load
query('.custom-map-load').attr('disabled', true);
query('.custom-map-delete').attr('disabled', true);
},
toggleForm: function (enabled) {
if (enabled === true) {
this.resetWebMap();
document.getElementById('txtMapName').disabled = false;
document.getElementById('btnSaveCustomMap').disabled = false;
document.getElementById('btnCancelCustomMap').disabled = false;
query('input[name="basemap-options"]').attr('disabled', false);
query('input[name="thematic-options"]').attr('disabled', false);
query('input[name="property-options"]').attr('disabled', false);
query('.custom-map-load').attr('disabled', true);
query('.custom-map-delete').attr('disabled', true);
}
else {
this.resetForm();
document.getElementById('txtMapName').disabled = true;
document.getElementById('btnSaveCustomMap').disabled = true;
document.getElementById('btnCancelCustomMap').disabled = true;
query('input[name="basemap-options"]').attr('disabled', true);
query('input[name="thematic-options"]').attr('disabled', true);
query('input[name="property-options"]').attr('disabled', true);
query('.custom-map-load').attr('disabled', false);
query('.custom-map-delete').attr('disabled', false);
}
},
resetForm: function () {
//clear the map name field
document.getElementById('txtMapName').value = '';
//set the default basemap
document.getElementById('basemap-radio-' + this.defaultBasemap).checked = true;
//clear the thematic layers
var thematicInputs = document.getElementsByName('thematic-options');
for (var i = 0, _l = thematicInputs.length; i < _l; i++) {
thematicInputs[i].checked = false;
}
//clear the operational layers
var propertyInputs = document.getElementsByName('property-options');
for (var i = 0, _l = propertyInputs.length; i < _l; i++) {
propertyInputs[i].checked = false;
}
},
save: function () {
//get and validate map name
var mapName = document.getElementById('txtMapName').value;
if (!mapName || mapName === null || mapName === '') {
domClass.remove('txt-map-name-warning', 'hidden');
domClass.add('div-map-name', 'bg-danger');
domClass.add('div-map-name', 'warning-container');
return;
}
else {
//reset warning styles
domClass.add('txt-map-name-warning', 'hidden');
domClass.remove('div-map-name', 'bg-danger');
domClass.remove('div-map-name', 'warning-container');
}
//build web map json from inputs
var webMapJson = this.getWebMapJsonTemplate();
webMapJson.item.title = mapName;
//get the selected basemap layer
var selectedBasemap = query('input[name="basemap-options"]:checked')[0];
for (var i = 0, _l = this.config.basemapLayers.length; i < _l; i++) {
var basemapJson = this.config.basemapLayers[i];
if (selectedBasemap.value === basemapJson.id) {
webMapJson.itemData.baseMap.baseMapLayers.push(basemapJson);
break;
}
}
//get the selected thematic layer
var selectedThematic = query('input[name="thematic-options"]:checked')[0];
if (selectedThematic && selectedThematic !== null) {
for (var i = 0, _l = this.config.thematicLayers.length; i < _l; i++) {
var thematicJson = this.config.thematicLayers[i];
if (selectedThematic.value === thematicJson.id) {
webMapJson.itemData.operationalLayers.push(thematicJson);
break;
}
}
}
//add the selected geography
var selectedGraphicsLayer = this.map.getLayer("selected-geography");
for (var i = 0, sgl = selectedGraphicsLayer.graphics.length; i < sgl; i++) {
//build the json object
var graphic = selectedGraphicsLayer.graphics[i];
var graphicObj = {
"id": "selected-geography",
"title": "Community",
"featureCollection": {
"layers": [
{
"layerDefinition": {
"geometryType": "esriGeometryPolygon",
"objectIdField": "OBJECTID",
"type": "Feature Layer",
"typeIdField": "",
"drawingInfo": {
"renderer": {
"type": "simple",
"symbol": graphic.symbol.toJson()
},
"fixedSymbols": true
},
"fields": [
{
"name": "OBJECTID",
"alias": "OBJECTID",
"type": "esriFieldTypeOID",
"editable": false,
"nullable": false,
"domain": null
}
],
"name": "Community"
},
"featureSet": {
"features": [
{
"geometry": graphic.geometry.toJson(),
"attributes": graphic.attributes
}
],
"geometryType": "esriGeometryPolygon"
}
}],
"showLegend": true
},
"visibility": true,
"opacity": selectedGraphicsLayer.opacity
};
webMapJson.itemData.operationalLayers.push(graphicObj);
}
//get the property layers
var selectedProperties = query('input[name="property-options"]:checked');
if (selectedProperties && selectedProperties !== null) {
for (var i = 0, _l = this.config.propertyLayers.length; i < _l; i++) {
var propertyJson = this.config.propertyLayers[i];
for (var x = 0, xl = selectedProperties.length; x < xl; x++) {
if (selectedProperties[x].value === propertyJson.id) {
webMapJson.itemData.operationalLayers.push(propertyJson);
break;
}
}
}
}
//get the map extent
var wgsExtent = webMercatorUtils.webMercatorToGeographic(this.map.extent);
var extent = [
[wgsExtent.xmin, wgsExtent.ymin],
[wgsExtent.xmax, wgsExtent.ymax]
]
webMapJson.item.extent = extent;
//store the webmapjson in local storage
this.webMapCollection.push({
name: mapName,
map: JSON.stringify(webMapJson)
});
if (this.useLocalStorage === true) {
window.localStorage.setItem(this.localStorageKey, JSON.stringify(this.webMapCollection))
}
//refresh the maps table
this.updateMapTable();
//reset the form and disable until the user re-enables
this.resetForm();
this.toggleForm(false);
document.getElementById('chkNewMap').checked = false;
},
load: function (mapDef) {
var self = this;
var returnD = new Deferred();
//hacky; fixing issue where height is reset on new map load
var mHeight = document.getElementById("mapDiv").style.height;
self.map.destroy();
//using HudWebApi MapController - gives the map some built in functionality.
var d = new Deferred();
var options = {
config: mapDef,
mapElementId: "mapDiv",
deferred: d
};
var mapController = new MapController(options);
d.then(function (map) {
self.map = map;
document.getElementById("mapDiv").style.height = mHeight;
self.map.resize();
//HACK - Fix for a bug in the JS API when loading
//a map from the WebMapJson specification that
//contains a featureCollection layer
//Esri seems to append an '_0' to the layer name, but
//does not update the layer name in the array of layer names:
//map.layerIds
var index = self.map.layerIds.indexOf("selected-geography");
if (index > -1) {
self.map.layerIds[index] = "selected-geography_0";
}
returnD.resolve(map);
self.emit("map-loaded", { map: map });
});
return returnD;
},
delete: function (mapIndex) {
this.webMapCollection.splice(parseInt(mapIndex), 1);
this.updateMapTable();
if (this.useLocalStorage === true) {
window.localStorage.setItem(this.localStorageKey, JSON.stringify(this.webMapCollection));
}
},
updateMapTable: function () {
var self = this;
var rowHtml = '';
for (var i = 0, _l = this.webMapCollection.length; i < _l; i++) {
rowHtml +=
'<tr>' +
'<td>' + (i + 1).toString() + '</td>' +
'<td style="width:75%;">' + this.webMapCollection[i].name + '</td>' +
'<td><button class="btn btn-default btn-xs custom-map-delete" value="' + i.toString() + '">Delete</button></td>' +
'<td><button class="btn btn-default btn-xs custom-map-load" value="' + i.toString() + '">Load</button>' +
'</tr>';
}
document.getElementById('custom-map-table').innerHTML = rowHtml;
var links = query('.custom-map-load');
links.on('click', function (e) {
//find the matching map
var mapDef = JSON.parse(self.webMapCollection[parseInt(e.target.value)].map);
self.load(mapDef);
});
var deleteBtns = query('.custom-map-delete');
deleteBtns.on('click', function (e) {
self.delete(e.target.value);
});
},
addThematicLayer: function (layerId) {
//remove existing thematic layer if it exists
if (this.selectedThematicLayer !== null) {
var id = this.selectedThematicLayer.id;
this.map.removeLayer(this.selectedThematicLayer);
if (this.map.layerIds.indexOf(id) > -1) { this.map.layerIds.splice(this.map.layerIds.indexOf(id), 1); }
}
//add new selected layer
for (var i = 0, _l = this.config.thematicLayers.length; i < _l; i++) {
var l = this.config.thematicLayers[i];
if (l.id === layerId) {
var thematicLayer = new FeatureLayer(l.url, { id: l.id });
thematicLayer.title = l.title;
this.map.addLayer(thematicLayer, 0);
this.selectedThematicLayer = thematicLayer;
if (this.map.layerIds.indexOf(thematicLayer.id) === -1) { this.map.layerIds.push(thematicLayer.id); }
break;
}
}
},
addPropertyLayer: function (layerId) {
//add new selected layer
for (var i = 0, _l = this.config.propertyLayers.length; i < _l; i++) {
var l = this.config.propertyLayers[i];
if (l.id === layerId) {
var layer = new FeatureLayer(l.url, { id: l.id });
layer.title = l.title;
this.map.addLayer(layer);
if (this.map.layerIds.indexOf(layer.id) === -1) { this.map.layerIds.push(layer.id); }
break;
}
}
},
removePropertyLayer: function (layerId) {
var layer = this.map.getLayer(layerId);
if (layer) {
var id = layer.id;
this.map.removeLayer(layer);
if (this.map.layerIds.indexOf(id) > -1) { this.map.layerIds.splice(this.map.layerIds.indexOf(id), 1); }
}
},
resetWebMap: function () {
var mapDef = this.getDefaultWebMap();
var d = this.load(mapDef);
var self = this;
d.then(function (map) {
if (self.defaultGraphic && self.defaultGraphic !== null) {
var gLayer = new GraphicsLayer({
id: "selected-geography",
opacity: 0.8
});
gLayer.name = "Community";
map.addLayer(gLayer);
gLayer.add(self.defaultGraphic);
self.map.setExtent(self.defaultGraphic.geometry.getExtent(), true);
}
self.emit("map-loaded", { map: map });
});
},
getWebMapJsonTemplate: function () {
var webMapJson = {
item: {
extent: [],
title: "HUD EGIS CART - CUSTOM MAP"
},
itemData: {
operationalLayers: [],
baseMap: {
baseMapLayers: [],
title: "Basemap"
},
version: "1.7"
}
};
return webMapJson;
},
getDefaultWebMap: function () {
var defaultWebMap = {
item: {
title: "HUD CART",
snippet: "HUD CART",
extent: [
[
-139.4916,
10.7191
],
[
-52.392,
59.5199
]
]
},
itemData: {
baseMap: {
baseMapLayers: [
{
id: "World_Dark_Gray_Base",
opacity: 1,
visibility: true,
url: "http://services.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer"
}
],
title: "gray"
},
version: "1.7"
}
}
return defaultWebMap;
},
localStorageAvailable: function () {
try {
var storage = window['localStorage'],
x = '__storage_test__';
storage.setItem(x, x);
storage.removeItem(x);
return true;
}
catch (e) {
return false;
}
},
getConfig: function () {
var d = new Deferred();
var url = 'config/mapBuilder.json';
var r = xhr(url, {
handleAs: 'json'
});
r.then(function (response) {
d.resolve(response);
},
function (err) {
//TODO
});
return d;
}
});
});