Reusable Accessible Mapping Platform

API Docs for: 3.0.0
Show:

File: src\js\RAMP\Modules\map.js

  1. /*global define, esri, dojoConfig, i18n, console, $ */
  2.  
  3. /**
  4. *
  5. * A RAMP Map module with ESRI and Dojo AMD Modules
  6. * This module provides function to create an ESRI web map control. A global config object is
  7. * required to initialize the map.
  8. *
  9. * @module RAMP
  10. * @submodule Map
  11. * @main Map
  12. */
  13.  
  14. /**
  15. * Map class represents the ESRI map object. The map is generated based on the application configuration and templates.
  16. *
  17. * @class Map
  18. * @uses dojo/_base/declare
  19. * @uses dojo/_base/array
  20. * @uses dojo/dom
  21. * @uses dojo/dom-class
  22. * @uses dojo/dom-construct
  23. * @uses dojo/number
  24. * @uses dojo/query
  25. * @uses dojo/_base/lang
  26. * @uses dojo/topic
  27. * @uses dojo/on
  28. * @uses esri/map
  29. * @uses esri/layers/FeatureLayer
  30. * @uses esri/layers/ArcGISTiledMapServiceLayer
  31. * @uses esri/layers/ArcGISDynamicMapServiceLayer
  32. * @uses esri/layers/WMSLayer
  33. * @uses esri/layers/WMSLayerInfo
  34. * @uses esri/tasks/GeometryService
  35. * @uses esri/tasks/ProjectParameters
  36. * @uses esri/geometry/Polygon
  37. * @uses esri/SpatialReference
  38. * @uses esri/dijit/Scalebar
  39. * @uses esri/geometry/Extent
  40. * @uses esri/graphicsUtils
  41. * @uses GlobalStorage
  42. * @uses RAMP
  43. * @uses FeatureClickHandler
  44. * @uses Navigation
  45. * @uses EventManager
  46. * @uses Util
  47. * @uses Array
  48. */
  49.  
  50. define([
  51. /* Dojo */
  52. "dojo/_base/declare", "dojo/_base/array", "dojo/dom", "dojo/dom-class",
  53. "dojo/dom-construct", "dojo/number", "dojo/query", "dojo/_base/lang", "dojo/topic", "dojo/on",
  54.  
  55. /* Esri */
  56. "esri/map", "esri/layers/FeatureLayer", "esri/layers/GraphicsLayer", "esri/layers/ArcGISTiledMapServiceLayer", "esri/layers/ArcGISDynamicMapServiceLayer",
  57. "esri/tasks/GeometryService", "esri/tasks/ProjectParameters", "esri/geometry/Polygon", "esri/SpatialReference", "esri/config",
  58. "esri/dijit/Scalebar", "esri/geometry/Extent", "esri/graphicsUtils", "esri/layers/WMSLayer", "esri/layers/WMSLayerInfo", "esri/request",
  59.  
  60. /* Ramp */
  61. "ramp/globalStorage", "ramp/ramp", "ramp/featureClickHandler", "ramp/mapClickHandler", "ramp/navigation", "ramp/eventManager",
  62.  
  63. /* Util */
  64. "utils/util", "utils/array", "utils/dictionary"],
  65.  
  66. function (
  67. /* Dojo */
  68. declare, dojoArray, dom, domClass, domConstruct, number, query, dojoLang, topic, dojoOn,
  69.  
  70. /* Esri */
  71. EsriMap, FeatureLayer, GraphicsLayer, ArcGISTiledMapServiceLayer, ArcGISDynamicMapServiceLayer,
  72. GeometryService, ProjectParameters, Polygon, SpatialReference, esriConfig,
  73. EsriScalebar, EsriExtent, esriGraphicUtils, WMSLayer, WMSLayerInfo, EsriRequest,
  74.  
  75. /* Ramp */
  76. GlobalStorage, Ramp, FeatureClickHandler, MapClickHandler, Navigation, EventManager,
  77.  
  78. /* Util */
  79. UtilMisc, UtilArray, UtilDict) {
  80. "use strict";
  81.  
  82. /**
  83. * An Array of {{#crossLink "Esri/layer/FeatureLayer"}}{{/crossLink}} objects.
  84. *
  85. * @private
  86. * @property featureLayers {Array}
  87. */
  88. var featureLayers,
  89.  
  90. /**
  91. * An Array of {{#crossLink "Esri/layer/WMSLayer"}}{{/crossLink}} objects.
  92. *
  93. * @private
  94. * @property wmsLayers {Array}
  95. */
  96. wmsLayers = [],
  97.  
  98. /**
  99. * Maps the id of a graphic layer to the GraphicsLayer Object that represents its extent bounding box.
  100. * A dictionary of String, {{#crossLink "Esri/layer/GraphicsLayer"}}{{/crossLink}} pairs.
  101. *
  102. * @private
  103. * @property boundingBoxMapping {Object}
  104. */
  105. boundingBoxMapping,
  106.  
  107. /**
  108. * The map not only contains feature layers, but also other layers such as the
  109. * basemap layer, highlight layer, bounding box layer, etc. This variable is
  110. * used to store the starting index of the feature layers in the map.
  111. *
  112. * @private
  113. * @property featureLayerStartIndex {Integer}
  114. */
  115. featureLayerStartIndex,
  116.  
  117. map,
  118.  
  119. fullExtent,
  120. maxExtent,
  121. initExtent;
  122.  
  123. /**
  124. * Shows the loading image.
  125. *
  126. * @private
  127. * @method _showLoadingImg
  128. */
  129. function _showLoadingImg() {
  130. $("#map-load-indicator").removeClass("hidden");
  131. }
  132.  
  133. /**
  134. * Hides the loading image.
  135. *
  136. * @private
  137. * @method _hideLoadingImg
  138. */
  139. function _hideLoadingImg() {
  140. $("#map-load-indicator").addClass("hidden");
  141. }
  142.  
  143. /**
  144. * Update Map Scale when zoom level changes
  145. *
  146. * @private
  147. * @method _updateScale
  148. * @param {Object} event
  149. */
  150. function _updateScale(event) {
  151. if (event.levelChange) {
  152. var currentScale = number.format(event.lod.scale),
  153. scaleLabelText = "1 : " + currentScale;
  154.  
  155. domConstruct.empty('scaleLabel');
  156. $("#scaleLabel").text(scaleLabelText);
  157. }
  158. }
  159.  
  160. /**
  161. * Initialize Map Scale
  162. *
  163. * @private
  164. * @method _initScale
  165. * @param {Object} event
  166. */
  167. function _initScale(event) {
  168. var map = event.map,
  169. scaleDiv = domConstruct.create("div", {
  170. id: "scaleDiv",
  171. class: "esriScalebarLabel"
  172. }),
  173. currentScale,
  174. scaleLabelText;
  175. $(scaleDiv).html("<span>" + i18n.t('map.scale') + "</span><br><span id='scaleLabel'><span/>");
  176. currentScale = number.format(map.getScale());
  177. scaleLabelText = "1 : " + currentScale;
  178.  
  179. domConstruct.place(scaleDiv, query(".esriScalebarRuler")[0], "before");
  180. domConstruct.empty('scaleLabel');
  181. $("#scaleLabel").text(scaleLabelText);
  182.  
  183. // Change the css class of the scale bar so it shows up against
  184. // the map
  185. topic.subscribe(EventManager.BasemapSelector.BASEMAP_CHANGED, function (attr) {
  186. $(".esriScalebar > div").removeClass().addClass(attr.cssStyle);
  187. });
  188. }
  189.  
  190. /**
  191. * Republishes map events to the outside using topic.publish
  192. *
  193. * @method _initRepublishers
  194. * @param {Object} map object
  195. * @private
  196. */
  197. function _initRepublishers(map) {
  198. var prefix = "map";
  199.  
  200. /**
  201. * Republish map events using topic.publish
  202. *
  203. * @method republish
  204. * @param {String} name
  205. * @private
  206. */
  207. function republish(name) {
  208. map.on(name, function (event) {
  209. topic.publish(prefix + "/" + name, event);
  210. });
  211. }
  212.  
  213. republish("update-end");
  214. republish("extent-change");
  215. republish("zoom-start");
  216. republish("zoom-end");
  217. republish("pan-start");
  218. republish("pan-end");
  219. }
  220.  
  221. /**
  222. * Subscribe to external events (published using topic.publish)
  223. * and react accordingly
  224. *
  225. * @method _initListeners
  226. * @param {Object} map map object
  227. * @private
  228. */
  229. function _initListeners(map) {
  230. /* SUBSCRIBED EVENTS */
  231. topic.subscribe(EventManager.Map.CENTER_AT, function (event) {
  232. map.centerAt(event.point);
  233. });
  234.  
  235. topic.subscribe(EventManager.Map.CENTER_AND_ZOOM, function (event) {
  236. var point = new esri.geometry.Point(event.graphic.geometry.x, event.graphic.geometry.y, map.spatialReference),
  237. d = map.centerAndZoom(point, event.level); // Last parameter is the level
  238.  
  239. if (event.callback) {
  240. d.then(event.callback);
  241. }
  242. });
  243.  
  244. topic.subscribe(EventManager.Map.SET_EXTENT, function (event) {
  245. event.extent.spatialReference = map.spatialReference;
  246. var d = map.setExtent(event.extent);
  247.  
  248. if (event.callback) {
  249. d.then(event.callback);
  250. }
  251. });
  252.  
  253. /* NAVIGATION EVENTS */
  254. topic.subscribe(EventManager.Navigation.PAN, function (event) {
  255. // event.direction panUp, panDown, panLeft, panRight
  256. // this same as calling map.panUp(), map.panDown(), map.panLeft(), map.panRight()
  257. map[event.direction]();
  258. });
  259.  
  260. topic.subscribe(EventManager.Navigation.ZOOM_STEP, function (event) {
  261. map.setLevel(map.getLevel() + event.level);
  262. });
  263.  
  264. topic.subscribe(EventManager.Navigation.ZOOM, function (event) {
  265. map.setLevel(event.level);
  266. });
  267.  
  268. topic.subscribe(EventManager.Navigation.FULL_EXTENT, function () {
  269. map.setExtent(fullExtent);
  270. });
  271.  
  272. /* GUI EVENTS */
  273. topic.subscribe(EventManager.GUI.LAYOUT_CHANGE, function () {
  274. map.resize(true);
  275. });
  276.  
  277. // Unhighlight the point when the subpanel is collapsed
  278. topic.subscribe(EventManager.GUI.SUBPANEL_CHANGE, function (eventArg) {
  279. // unhighlight only if the panel closing is the details panel
  280. if (!eventArg.visible &&
  281. eventArg.isComplete &&
  282. (eventArg.origin === ("rampPopup" || "datagrid"))) {
  283. topic.publish(EventManager.FeatureHighlighter.HIGHLIGHT_HIDE, {});
  284. }
  285. });
  286.  
  287. /* START BOUNDING BOX TOGGLE */
  288.  
  289. topic.subscribe(EventManager.FilterManager.LAYER_VISIBILITY_TOGGLED, function (evt) {
  290. var setTo = evt.state,
  291. layerId = evt.id,
  292. // either take url (would need mapping to layer on map),
  293. // map id in config, graphic layer id
  294. layer = map.getLayer(layerId);
  295.  
  296. layer.setVisibility(setTo);
  297. //loops through any static layers that are mapped to the feature layer being toggled
  298. try {
  299. dojoArray.forEach(GlobalStorage.LayerMap[layerId], function (staticLayer) {
  300. var layer = map.getLayer(staticLayer);
  301. layer.setVisibility(setTo);
  302. });
  303. }
  304. catch (err) {
  305. }
  306. });
  307.  
  308. topic.subscribe(EventManager.FilterManager.LAYER_TRANSPARENCY_CHANGED, function (evt) {
  309. var layer = map.getLayer(evt.layerId);
  310.  
  311. layer.setOpacity(evt.value);
  312. //loops through any static layers that are mapped to the feature layer being toggled
  313. try {
  314. dojoArray.forEach(GlobalStorage.LayerMap[evt.layerId], function (staticLayer) {
  315. var layer = map.getLayer(staticLayer);
  316. layer.setOpacity(evt.value);
  317. });
  318. }
  319. catch (err) {
  320. }
  321. });
  322.  
  323. topic.subscribe(EventManager.FilterManager.BOX_VISIBILITY_TOGGLED, function (evt) {
  324. setBoundingBoxVisibility(evt.id, evt.state);
  325. });
  326. /* END BOUNDING BOX TOGGLE */
  327.  
  328. topic.subscribe(EventManager.FilterManager.SELECTION_CHANGED, function (evt) {
  329. // this is handling the user trying to re-order the layers
  330. // graphical and feature layers must be handled separately from
  331. // all other layers as ESRI separates the two internally
  332.  
  333. var newIndex = evt.index,
  334. featureLayers;
  335.  
  336. if (map.layerIds.contains(evt.id)) {
  337. featureLayers = dojoArray.map(map.graphicsLayerIds, function (x) {
  338. return map.getLayer(x).type === 'Feature Layer' ? 1 : 0;
  339. }).sum();
  340. newIndex += 1 - featureLayers; // offset by 1 basemap not accounted for
  341. console.log('newIndex ' + newIndex);
  342. console.log(map.layerIds);
  343. } else {
  344. if (!featureLayerStartIndex) {
  345. // Find the index of the first feature layer
  346. featureLayerStartIndex = UtilArray.indexOf(map.graphicsLayerIds, function (layerId) {
  347. var layer = map.getLayer(layerId);
  348. return layer.type && layer.type === "Feature Layer";
  349. });
  350. }
  351. newIndex += featureLayerStartIndex;
  352. }
  353. map.reorderLayer(map.getLayer(evt.id), newIndex);
  354. topic.publish(EventManager.Map.REORDER_END);
  355. });
  356.  
  357. /* Add Layer subscription*/
  358. topic.subscribe(EventManager.Map.ADD_LAYER, function () {
  359. var type = dom.byId("addLayer-select-type").value,
  360. URL = dom.byId("addLayer-URL-input").value,
  361. opacity = dom.byId("addLayer-Opacity").value;
  362.  
  363. console.log(type + " | " + URL + " | " + opacity);
  364. addStaticLayer(type, URL, opacity);
  365. });
  366.  
  367. topic.subscribe(EventManager.Map.ADD_LAYER_READY, function (temp_layer) {
  368. map.addLayer(temp_layer);
  369. });
  370. }
  371. /**
  372. * Creates event handlers for the map control: click, mouse-over, load, extent change, and update events.
  373. *
  374. * @private
  375. * @method _initEventHandlers
  376. * @param {Object} map A ESRI map object
  377. */
  378. function _initEventHandlers(map) {
  379. var handle,
  380. // filter out non static layers for any feature interaction: maptip
  381. nonStaticLayers = dojoArray.filter(featureLayers, function (layer) {
  382. return layer.ramp.type !== GlobalStorage.layerType.Static;
  383. }
  384. );
  385.  
  386. // original value : featureLayers
  387. // updated with nonStaticLayer
  388. dojoArray.forEach(nonStaticLayers, function (fl) {
  389. //TODO: set timer for maptips onMouseOver event
  390.  
  391. fl.on("click", function (evt) {
  392. evt.stopImmediatePropagation();
  393. FeatureClickHandler.onFeatureSelect(evt);
  394. });
  395.  
  396. fl.on("mouse-over", function (evt) {
  397. FeatureClickHandler.onFeatureMouseOver(evt);
  398.  
  399. //console.log("hover on point", evt);
  400. });
  401.  
  402. fl.on("mouse-out", function (evt) {
  403. FeatureClickHandler.onFeatureMouseOut(evt);
  404. });
  405. });
  406.  
  407. map.on("load", _initScale);
  408. map.on("extent-change", function (event) {
  409. _updateScale(event);
  410.  
  411. console.log("map - >> extent-change", event);
  412. dojoOn.once(map, "update-end", function () {
  413. console.log("map - >> update-end - >> Apply extent Filter");
  414. topic.publish(EventManager.Datagrid.APPLY_EXTENT_FILTER);
  415. });
  416. });
  417.  
  418. // Deselect all highlighted points if the map is clicked
  419. map.on("click", function (evt) {
  420. FeatureClickHandler.onFeatureDeselect(evt);
  421. topic.publish(EventManager.Map.CLICK, evt);
  422. });
  423.  
  424. // Hide all the maptips if the map finishes updating
  425. map.on("update-end", function () {
  426. //topic.publish(EventManager.Maptips.HIDE, {});
  427. });
  428.  
  429. // Show/Hide spinner for map loading
  430. map.on("update-start", _showLoadingImg);
  431. map.on("update-end", _hideLoadingImg);
  432.  
  433. handle = map.on("update-end", function () {
  434. var isAllLoaded = dojoArray.every(
  435. map.graphicsLayerIds.concat(map.layerIds),
  436. function (layerId) {
  437. var layer = map.getLayer(layerId);
  438. //console.log(layer.loaded, layerId, layer);
  439. return layer.loaded;
  440. }
  441. );
  442.  
  443. console.log("map -> is all layers loaded: ", isAllLoaded);
  444.  
  445. if (isAllLoaded) {
  446. handle.remove();
  447. console.log("map ->", EventManager.Map.ALL_LAYERS_LOADED);
  448. topic.publish(EventManager.Map.ALL_LAYERS_LOADED);
  449. }
  450. });
  451. }
  452.  
  453. /**
  454. * Instantiates an extent from a JSON config object and spatial reference
  455. *
  456. * @private
  457. * @method createExtent
  458. * @param {Object} extentConfig the JSON config object
  459. * @param {Esri/SpatialReference} sr the {{#crossLink "Esri/SpatialReference"}}{{/crossLink}}
  460. * @return {esri/geometry/Extent} An ESRI extent object based on the config data
  461. */
  462. function createExtent(extentConfig, sr) {
  463. return new EsriExtent(
  464. extentConfig.xmin, extentConfig.ymin, extentConfig.xmax, extentConfig.ymax, sr);
  465. }
  466.  
  467. /**
  468. * Add a static, non-interactive Layer to the map
  469. *
  470. * @private
  471. * @method AddStaticLayer
  472. * @param {String} layer_type A value which controls how the layer is going to be added to the map
  473. * @param {String} layer_url A URL pointing to a valid map service endpoint
  474. * @param {Number} layer_op A value between 0.0 and 1.0 which determines the transparency of the layer
  475. */
  476. function addStaticLayer(layer_type, layer_url, layer_op) {
  477. layer_op = layer_op / 100; // change percentage to decimal
  478. var tempLayer;
  479. switch (layer_type) {
  480. case "feature":
  481. tempLayer = new FeatureLayer(layer_url, {
  482. opacity: layer_op,
  483. mode: FeatureLayer.MODE_SNAPSHOT
  484. });
  485. break;
  486.  
  487. case "tile":
  488. tempLayer = new ArcGISTiledMapServiceLayer(layer_url, {
  489. opacity: layer_op
  490. });
  491. break;
  492.  
  493. case "dynamic":
  494. tempLayer = new ArcGISDynamicMapServiceLayer(layer_url, {
  495. opacity: layer_op
  496. });
  497. break;
  498.  
  499. default:
  500.  
  501. break;
  502. }
  503.  
  504. topic.publish(EventManager.Map.ADD_LAYER_READY, tempLayer);
  505. topic.publish(EventManager.GUI.ADD_LAYER_PANEL_CHANGE, {
  506. visible: false
  507. });
  508. }
  509.  
  510. /**
  511. * Sets the visibility of the bounding box that belongs to the layer with the given layerId.
  512. * Note: the layerId needs to be the ID of the featurelayer, not the ID of the actual bounding
  513. * box layer.
  514. *
  515. * @private
  516. * @method setBoundingBoxVisibility
  517. * @param {String} layerId the id of the layer whose bounding box visibility should be set
  518. */
  519. function setBoundingBoxVisibility(layerId, visibility) {
  520. var boxLayer = boundingBoxMapping[layerId];
  521.  
  522. //if (boxLayer.graphics.isEmpty() && visibility) {
  523. // // get bounding box info from config object
  524. // var boundingBoxExtent;
  525. // var layerConfig = dojoArray.find(config.featureLayers, function (layerConfig) {
  526. // return layerConfig.id === layerId;
  527. // });
  528.  
  529. // boundingBoxExtent.xmin = layerConfig.boundingBox.extent.xmin;
  530. // boundingBoxExtent.ymin = layerConfig.boundingBox.extent.ymin;
  531. // boundingBoxExtent.xmax = layerConfig.boundingBox.extent.xmax;
  532. // boundingBoxExtent.ymax = layerConfig.boundingBox.extent.ymax;
  533.  
  534. // var extentGraphic = new esri.Graphic({
  535. // geometry: boundingBoxExtent,
  536. // symbol: {
  537. // color: [255, 0, 0, 64],
  538. // outline: {
  539. // color: [240, 128, 128, 255],
  540. // width: 1,
  541. // type: "esriSLS",
  542. // style: "esriSLSSolid"
  543. // },
  544. // type: "esriSFS",
  545. // style: "esriSFSSolid"
  546. // }
  547. // });
  548. // boxLayer.add(extentGraphic);
  549. //}
  550.  
  551. boxLayer.setVisibility(visibility);
  552. }
  553.  
  554. function resolveLayerOpacity(layerOpacity) {
  555. return layerOpacity.default || 1;
  556. }
  557.  
  558. function generateStaticLayer(staticLayer) {
  559. var tempLayer;
  560. //determine layer type and process
  561. switch (staticLayer.layerType) {
  562. case "feature":
  563. tempLayer = new FeatureLayer(staticLayer.url, {
  564. opacity: resolveLayerOpacity(staticLayer.settings.opacity),
  565. mode: FeatureLayer.MODE_SNAPSHOT,
  566. id: staticLayer.id
  567. });
  568. tempLayer.ramp = {
  569. type: GlobalStorage.layerType.Static
  570. };
  571. break;
  572.  
  573. case "tile":
  574. tempLayer = new ArcGISTiledMapServiceLayer(staticLayer.url, {
  575. opacity: resolveLayerOpacity(staticLayer.settings.opacity),
  576. id: staticLayer.id
  577. });
  578. console.log("tile layer added. " + staticLayer.id);
  579. break;
  580.  
  581. case "dynamic":
  582. tempLayer = new ArcGISDynamicMapServiceLayer(staticLayer.url, {
  583. opacity: resolveLayerOpacity(staticLayer.settings.opacity),
  584. id: staticLayer.id
  585. });
  586. console.log("dynamic layer added. " + staticLayer.id);
  587. break;
  588.  
  589. default:
  590. //TODO add in other types of maps... wms? non-esri tile?
  591. break;
  592. }
  593. return tempLayer;
  594. }
  595.  
  596. return {
  597. /**
  598. * The maximum extent of the map control is allowed to go to
  599. * @property getMaxExtent
  600. * @type {Object}
  601. *
  602. */
  603. getMaxExtent: function () {
  604. return maxExtent;
  605. },
  606. /**
  607. * Return the map control object
  608. * @property getMap
  609. * @type {Object}
  610. *
  611. */
  612. getMap: function () {
  613. if (UtilMisc.isUndefined(map)) {
  614. console.log("trying to get map before it is available!");
  615. }
  616. return map;
  617. },
  618.  
  619. /**
  620. * Returns a list of feature layers that are currently visible on the map.
  621. * @method getVisibleFeatureLayers
  622. * @return {Array} an array of {{#crossLink "Esri/layer/FeatureLayer"}}{{/crossLink}} objects
  623. *
  624. */
  625. getVisibleFeatureLayers: function () {
  626. // Return only the feature layers
  627. //TODO do we need to consider static layers here?
  628. return dojoArray.filter(map.getLayersVisibleAtScale(), function (layer) {
  629. return layer.type && (layer.type === "Feature Layer") && layer.visible;
  630. });
  631. },
  632.  
  633. /**
  634. * Return the feature layer corresponding to the given url.
  635. *
  636. * @method getFeatureLayer
  637. * @private
  638. * @param {String} featureUrl the url of the feature layer
  639. * @return {Esri/layer/FeatureLayer} feature layer
  640. */
  641. getFeatureLayer: function (featureUrl) {
  642. return UtilArray.find(featureLayers,
  643. function (featureLayer) {
  644. return featureLayer.url === featureUrl;
  645. });
  646. },
  647.  
  648. /**
  649. * Given an ESRI Extent Object, returns a new ESRI Extent Object that
  650. * contains the extent adjusted according to this map's maximum extent
  651. *
  652. * NOTE: this method is currently unused!
  653. *
  654. * @param {esri/geometry/Extent} e the extent Object
  655. * @param {esri/geometry/Extent} maxExtent the maximum extent
  656. * @return {esri/geometry/Extent} An adjusted extent, if the target extent is outside the boundary
  657. * @method checkBoundary
  658. */
  659. checkBoundary: function (e, maxExtent) {
  660. var extent = e,
  661. width = extent.width(),
  662. height = extent.height(),
  663. centerX = extent.centerX(),
  664. centerY = extent.centerY(),
  665. flag, adjustedEx;
  666.  
  667. adjustedEx = extent.clone();
  668.  
  669. var maxHeight = maxExtent.height();
  670. if (height > maxHeight) {
  671. height = maxHeight;
  672. }
  673.  
  674. if (centerY > maxExtent.ymax) {
  675. adjustedEx.ymax = maxExtent.ymax;
  676. adjustedEx.ymin = maxExtent.ymax - height;
  677. flag = true;
  678. //} else if (extent.ymin < maxExtent.ymin) {
  679. } else if (centerY < maxExtent.ymin) {
  680. adjustedEx.ymin = maxExtent.ymin;
  681. adjustedEx.ymax = maxExtent.ymin + height;
  682. flag = true;
  683. }
  684.  
  685. var maxWidth = maxExtent.width();
  686. if (width > maxWidth) {
  687. width = maxWidth;
  688. }
  689.  
  690. if (centerX > maxExtent.xmax) {
  691. adjustedEx.xmax = maxExtent.xmax;
  692. adjustedEx.xmin = maxExtent.xmax - width;
  693. flag = true;
  694. } else if (centerX < maxExtent.xmin) {
  695. adjustedEx.xmin = maxExtent.xmin;
  696. adjustedEx.xmax = maxExtent.xmin + width;
  697. flag = true;
  698. }
  699.  
  700. if (flag) {
  701. return adjustedEx;
  702. }
  703. },
  704. /*
  705. * Initialize map control with configuration objects provided in the bootstrapper.js file.
  706. *
  707. * Initialize extent
  708. * Add base map from the config.basemaps array that has the showOnInit()
  709. * Add Static layer from config.featureLayers.staticLayers
  710. * Add feature layers from config.featureLayers
  711. * Create bounding layers and add to map control
  712. * Add map tip events to each feature layer (click/hover/out)
  713. * Show scalebar
  714. * Publish events to outside for other modules to use
  715. * Subscribe events to update map control
  716. *
  717. * Note: Not sure if we want to include all the config requirements here.
  718. * Map control is initialized with div id provided. The following config file entries are used:
  719. * config.spatialReference
  720. * config.extents.defaultExtent xmin, ymin, xmax, ymax
  721. * config.levelOfDetails.minLevel
  722. * config.levelOfDetails.maxLevel
  723. * config.extents.maximumExtent
  724. * config.extents.fullExtent
  725. * config.basemaps arrays of basemap, only one or first one with showOnInit set to true
  726. * config.featureLayers
  727. *
  728. * @method init
  729. * @param {Object} mapDiv the HTML div that will store the map control
  730. * @constructor
  731. *
  732. */
  733. init: function () {
  734. //config object is loaded in bootstrapper.js
  735. var config = GlobalStorage.config,
  736.  
  737. /**
  738. * The spatial reference of the map
  739. *
  740. * @property spatialReference
  741. * @private
  742. * @type {esri/SpatialReference}
  743. */
  744. spatialReference = new esri.SpatialReference(config.spatialReference),
  745.  
  746. /**
  747. * The URL of the basemap that is on by default
  748. *
  749. * @property url
  750. * @private
  751. * @type {String}
  752. */
  753. url = UtilArray.find(config.basemaps, function (basemap) {
  754. return basemap.showOnInit;
  755. }).url,
  756.  
  757. /**
  758. * The basemap layer
  759. *
  760. * @property baseLayer
  761. * @private
  762. * @type {Esri/layers/ArcGISTiledMapServiceLayer}
  763. */
  764. baseLayer = new ArcGISTiledMapServiceLayer(url, {
  765. id: "basemapLayer"
  766. });
  767.  
  768. /**
  769. * The maximum extent of the map
  770. *
  771. * @property maxExtent
  772. * @private
  773. * @type {esri/geometry/Extent}
  774. */
  775. maxExtent = createExtent(config.extents.maximumExtent, spatialReference);
  776.  
  777. /**
  778. * The initial extent of the map
  779. *
  780. * @property InitExtent
  781. * @private
  782. * @type {esri/geometry/Extent}
  783. */
  784. initExtent = createExtent(config.extents.defaultExtent, spatialReference);
  785.  
  786. /**
  787. * Used for full extent in nav widget
  788. *
  789. * @property fullExtent
  790. * @private
  791. * @type {esri/geometry/Extent}
  792. */
  793. fullExtent = createExtent(config.extents.fullExtent, spatialReference);
  794.  
  795. esriConfig.defaults.io.proxyUrl = GlobalStorage.config.proxyUrl;// "/proxy/proxy.ashx";
  796. dojoConfig.ecfg = esriConfig;
  797. //generate WMS layers array
  798. wmsLayers = dojoArray.map(config.wmsLayers, function (layer) {
  799. var wmsl = new WMSLayer(layer.url, {
  800. id: layer.id,
  801. format: layer.format,
  802. opacity: resolveLayerOpacity(layer.settings.opacity),
  803. resourceInfo: {
  804. extent: new EsriExtent(layer.extent),
  805. layerInfos: [new WMSLayerInfo(layer.layerInfo)]
  806. }
  807. });
  808. wmsl.ramp = {
  809. type: GlobalStorage.layerType.WMS
  810. };
  811.  
  812. // WMS binding for getFeatureInfo calls
  813.  
  814. if (layer.featureInfo !== undefined) {
  815. console.log('registering ' + layer.displayName + ' for WMS getFeatureInfo');
  816. MapClickHandler.registerWMSClick({wmsLayer: wmsl, layerConfig: layer});
  817. }
  818.  
  819. wmsl.setVisibleLayers(layer.layerInfo.name);
  820. wmsl.setVisibility(layer.layerVisible);
  821.  
  822. return wmsl;
  823. });
  824.  
  825. //generate feature layers array
  826. featureLayers = dojoArray.map(config.featureLayers, function (layerConfig) {
  827. var fl;
  828.  
  829. if (layerConfig.isStatic) {
  830. fl = generateStaticLayer(layerConfig);
  831. } else {
  832. fl = new FeatureLayer(layerConfig.url, {
  833. id: layerConfig.id,
  834. mode: FeatureLayer.MODE_SNAPSHOT,
  835. outFields: [layerConfig.layerAttributes],
  836. visible: layerConfig.layerVisible,
  837. opacity: resolveLayerOpacity(layerConfig.settings.opacity)
  838. });
  839. fl.ramp = {
  840. type: GlobalStorage.layerType.Feature
  841. };
  842. if (layerConfig.boundingBoxVisible === true) {
  843. dojoOn.once(fl, "update-end", function () {
  844. setBoundingBoxVisibility(layerConfig.id, true);
  845. });
  846. }
  847. }
  848. if (layerConfig.layerVisible === false) {
  849. dojoOn.once(fl, "update-end", function () {
  850. fl.setVisibility(false);
  851. });
  852. }
  853. return fl;
  854. });
  855.  
  856. /**
  857. * A list GraphicsLayer that represent the extent bounding box of the feature layers.
  858. * {[esr/layer/featurelayers]} featureLayers A list of feature layers found in the application config
  859. * {[esri/layer/graphiclayer]} An array of graphic layers to add to the map
  860. *
  861. * @property boundingBoxLayers
  862. * @type {array of esri/layer/GraphicsLayer}
  863. */
  864.  
  865. var boundingBoxLayers = dojoArray.map(config.featureLayers, function (layer) {
  866. // Map a list of featurelayers into a list of GraphicsLayer representing
  867. // the extent bounding box of the feature layer. Note each bounding box layer
  868. // at this point are empty, the actual graphic that represent the bounding box
  869. // will be generated the first time the user toggles it on.
  870. var boundingBox = new GraphicsLayer({
  871. id: String.format("boundingBoxLayer_{0}", layer.id),
  872. visible: layer.boundingBoxVisible
  873. });
  874. boundingBox.ramp = {
  875. type: GlobalStorage.layerType.BoundingBox
  876. };
  877.  
  878. var boundingBoxExtent;
  879. if (typeof layer.boundingBox !== "undefined") {
  880.  
  881. boundingBoxExtent = createExtent(layer.boundingBox.extent, spatialReference);
  882.  
  883. var extentGraphic = new esri.Graphic({
  884. geometry: boundingBoxExtent,
  885. symbol: {
  886. color: [255, 0, 0, 64],
  887. outline: {
  888. color: [240, 128, 128, 255],
  889. width: 1,
  890. type: "esriSLS",
  891. style: "esriSLSSolid"
  892. },
  893. type: "esriSFS",
  894. style: "esriSFSSolid"
  895. }
  896. });
  897.  
  898. boundingBox.add(extentGraphic);
  899. }
  900. return boundingBox;
  901. });
  902.  
  903. // Maps layerId to a GraphicsLayer Object that represents the extent bounding box
  904. // for that layer
  905. boundingBoxMapping = UtilDict.zip(dojoArray.map(config.featureLayers, function (layer) {
  906. return layer.id;
  907. }), boundingBoxLayers);
  908.  
  909. //the map!
  910. map = new EsriMap(config.divNames.map, {
  911. extent: initExtent,
  912. logo: false,
  913. minZoom: config.levelOfDetails.minLevel,
  914. maxZoom: config.levelOfDetails.maxLevel,
  915. slider: false
  916. });
  917.  
  918. GlobalStorage.map = map;
  919. MapClickHandler.init(map);
  920.  
  921. /* START - Add static layers */
  922.  
  923. var staticLayers = [],
  924. perLayerStaticMaps = [],
  925. staticLayerMap = [];
  926.  
  927. dojoArray.forEach(config.featureLayers, function (layer) {
  928. perLayerStaticMaps = [];
  929. dojoArray.forEach(layer.staticLayers, function (staticLayer, i) {
  930. var tempLayer = map.generateStaticLayer(staticLayer);
  931.  
  932. staticLayers.push(tempLayer);
  933. //creates an array of all static layers defined for the current, single feature layer
  934. perLayerStaticMaps[i] = staticLayer.id;
  935. });
  936. //adds the static layer id array as a value to an array indexed by feature layer id
  937. staticLayerMap[layer.id] = perLayerStaticMaps;
  938. });
  939.  
  940. GlobalStorage.LayerMap = staticLayerMap;
  941. /* End - Add static layers */
  942.  
  943. baseLayer.ramp = {
  944. type: GlobalStorage.layerType.Basemap
  945. };
  946. // Combine all layer arrays then add them all at once (for efficiency)
  947. map.addLayers([baseLayer].concat(wmsLayers, staticLayers, boundingBoxLayers, featureLayers));
  948.  
  949. /* Start - Show scalebar */
  950. var scalebar = new EsriScalebar({
  951. map: map,
  952. attachTo: "bottom-left",
  953. scalebarUnit: "metric"
  954. });
  955.  
  956. scalebar.show();
  957.  
  958. /* End - Show scalebar */
  959.  
  960. _initRepublishers(map);
  961. _initListeners(map);
  962. _initEventHandlers(map, featureLayers);
  963. }
  964. };
  965. });