Reusable Accessible Mapping Platform

API Docs for: 5.0.0
Show:

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

  1. /*global define, console, $, RAMP */
  2.  
  3. //the "use strict" forces the ECMA Script 5 interpretation of the code
  4.  
  5. /**
  6. * QuickZoom submodule
  7. *
  8. * @module RAMP
  9. * @submodule QuickZoom
  10. * @main QuickZoom
  11. */
  12.  
  13. /**
  14. * The QuickZoom class handles zooming in the map based on province, city, or postal code.
  15. * These zoom in services rely on web services which return extent values based the user-entered province, city, or postal code
  16. *
  17. * @class QuickZoom
  18. * @uses dojo/_base/declare
  19. * @uses dojo/_base/array
  20. * @uses dojo/_base/lang
  21. * @uses dojo/dom
  22. * @uses dojo/dom-construct
  23. * @uses dijit/form/Form
  24. * @uses dijit/form/TextBox
  25. * @uses dijit/form/Select
  26. * @uses dijit/form/Button
  27. * @uses esri/geometry/Extent
  28. * @uses esri/tasks/QueryTask
  29. * @uses esri/tasks/query
  30. * @uses GlobalStorage
  31. * @uses Map
  32. * @uses Util
  33. */
  34.  
  35. define([
  36. /* Dojo */
  37. "dojo/_base/declare", "dojo/_base/array", "dojo/_base/lang", "dojo/dom", "dojo/dom-construct", "dijit/form/Form",
  38. "dijit/form/TextBox", "dijit/form/Select", "dijit/form/Button",
  39. /* Esri */
  40. "esri/geometry/Extent", "esri/tasks/QueryTask", "esri/tasks/query",
  41. /* Ramp */
  42. "ramp/globalStorage", "ramp/map",
  43. /* Util */
  44. "utils/util"],
  45.  
  46. function (
  47. /* Dojo */
  48. declare, dojoArray, dojoLang, dom, domConstruct, Form, TextBox, Select, Button,
  49. Extent, QueryTask, Query,
  50. /* Ramp */
  51. GlobalStorage, RampMap,
  52. /* Util */
  53. UtilMisc) {
  54. "use strict";
  55. return declare(null, {
  56. /*
  57. * Defines the UI controls for the province, city, and postal code selections
  58. * @method constructor
  59. * @constructor
  60. *
  61. */
  62. constructor: function () {
  63. this.config = RAMP.config;
  64.  
  65. this.form = new Form({
  66. style: "overflow:hidden; clear:none;"
  67. });
  68.  
  69. var className = "quickZoom"; // used for CSS styling
  70.  
  71. this.provinceSelect = new Select({
  72. id: "quickZoomProvince",
  73. class: className,
  74. options: []
  75. });
  76.  
  77. this.citySelect = new Select({
  78. id: "quickZoomCity",
  79. class: className,
  80. options: []
  81. });
  82.  
  83. this.postalCodeTextbox = new TextBox({
  84. id: "quickZoomPostalCode",
  85. class: className,
  86. style: "width : 30%"
  87. });
  88.  
  89. this.button = new Button({
  90. label: "Find",
  91. id: "quickZoomButton",
  92. class: className
  93. });
  94.  
  95. var that = this; // for local access to "this"
  96. function _addNode(domNode) {
  97. that.form.domNode.appendChild(domNode);
  98. }
  99.  
  100. function _addLabel(text) {
  101. var node = domConstruct.create("label", {
  102. class: className,
  103. innerHTML: text
  104. });
  105. _addNode(node);
  106. return node;
  107. }
  108.  
  109. _addLabel("Choose province:");
  110. _addNode(this.provinceSelect.domNode);
  111.  
  112. _addLabel("City:");
  113. _addNode(this.citySelect.domNode);
  114.  
  115. _addLabel("or enter postal code (e.g. A1A):");
  116. _addNode(this.postalCodeTextbox.domNode);
  117.  
  118. _addNode(this.button.domNode);
  119. this.errorText = _addLabel("");
  120. },
  121.  
  122. _setError: function (errorMsg) {
  123. console.log(errorMsg);
  124. $(this.errorText).text(errorMsg);
  125. },
  126. /*
  127. * This adds the search tools to the UI and populates the UI controls: Province dropdown, city drop down, postal code text box
  128. *
  129. * @method init
  130. * @param {Object} where A DOM object where the dropdowns will be placed
  131. * @constructor
  132. */
  133. init: function (where) {
  134. var provinceSelect = this.provinceSelect,
  135. citySelect = this.citySelect,
  136. config = this.config,
  137. that = this; // for local access to "this"
  138.  
  139. /**
  140. * Change the extent of the map based on the extent data
  141. * retrieved from the given url
  142. *
  143. * @method changeExtent
  144. * @private
  145. * @param {String} url
  146. * @param {Object} query
  147. */
  148. function changeExtent(url, query) {
  149. query.returnGeometry = true;
  150.  
  151. var queryTask = new QueryTask(url);
  152. queryTask.execute(query,
  153. function (featureSet) {
  154. if (featureSet.features.isEmpty()) {
  155. that._setError("invalid query");
  156. return;
  157. }
  158. var extent = featureSet.features[0].geometry.getExtent();
  159. if (RampMap.getMaxExtent().contains(extent)) {
  160. RampMap.getMap().setExtent(extent);
  161. that._setError("");
  162. } else {
  163. that._setError("beyond max extent");
  164. }
  165. },
  166. function (error) {
  167. console.log("Could not load extent from service");
  168. console.log(error);
  169. });
  170. }
  171.  
  172. /**
  173. * Populate the given dropdown with data from the given url.
  174. *
  175. * @method populateDropDown
  176. * @param {String} url the url to the service containing the data to populate the dropdown
  177. * @param {DObject} select the dojo Select object to populate
  178. * @param {Object} query to execute
  179. * @param {Function} mapFunc the function to convert each element in the retrieved data to a label that can be added to the dropdown menu
  180. */
  181. function populateDropDown(url, select, query, mapFunc) {
  182. // Clear the dropdown
  183. select.removeOption(select.getOptions());
  184.  
  185. var queryTask = new QueryTask(url);
  186. queryTask.execute(query,
  187. function (featureSet) {
  188. // Populate the dropdown from a list retrieved
  189. // from service
  190. select.addOption(dojoArray.map(featureSet.features, mapFunc));
  191. },
  192. function (error) {
  193. console.log("Could not populate dropdown");
  194. console.log(error);
  195. });
  196. }
  197.  
  198. // Populate the province dropdown from a list retrieved
  199. // from service
  200. provinceSelect.loadDropDown(function () {
  201. var provinceConfig = config.quickzoom.province,
  202. query = new Query();
  203.  
  204. query.where = "OBJECTID>0";
  205. query.outFields = [provinceConfig.shortName, provinceConfig.name];
  206.  
  207. populateDropDown(provinceConfig.url, provinceSelect, query,
  208. function (feature) {
  209. var shortName = feature.attributes[provinceConfig.shortName];
  210. return {
  211. label: feature.attributes[provinceConfig.name],
  212. value: shortName,
  213. selected: shortName === provinceConfig.selectedProv
  214. };
  215. });
  216. });
  217.  
  218. /**
  219. * Populates the city dropdown menu with the cities in the
  220. * selected province.
  221. *
  222. * @method populateCityDropDown
  223. * @private
  224. * @param {String} prov
  225. */
  226. function populateCityDropDown(prov) {
  227. var cityConfig = config.quickzoom.city,
  228. query = new Query();
  229.  
  230. query.where = UtilMisc.getWhereClause(cityConfig.province, prov);
  231. query.outFields = [cityConfig.name, cityConfig.id];
  232. populateDropDown(cityConfig.url, citySelect, query,
  233. function (feature) {
  234. return {
  235. label: feature.attributes[cityConfig.name],
  236. value: feature.attributes[cityConfig.id],
  237. selected: false
  238. };
  239. });
  240. }
  241.  
  242. citySelect.loadDropDown(function () {
  243. // Populate with the cities of the default province first
  244. populateCityDropDown(config.quickzoom.province.selectedProv);
  245. });
  246.  
  247. provinceSelect.on("change", function () {
  248. // Change the extent, then populate the city with the cities in the province
  249. var provConfig = config.quickzoom.province,
  250. prov = provinceSelect.get("value"),
  251. query = new Query();
  252. query.where = UtilMisc.getWhereClause(provConfig.shortName, prov);
  253.  
  254. changeExtent(provConfig.url, query);
  255. populateCityDropDown(prov);
  256. });
  257.  
  258. citySelect.on("change", function () {
  259. var cityConfig = config.quickzoom.city,
  260. city = citySelect.get("value"),
  261. query = new Query();
  262. query.where = UtilMisc.getWhereClause(cityConfig.id, city);
  263.  
  264. changeExtent(cityConfig.url, query);
  265. });
  266.  
  267. /**
  268. * Returns true if the given postal code is valid, false otherwise.
  269. *
  270. * @method validatePostalCode
  271. * @private
  272. * @param {String} fsa fsa
  273. */
  274. function validatePostalCode(fsa) {
  275. //Perform case insensitive matching
  276. var regexp = /[abcdefghijklmnopqrstuvwxyz]\d[abcdefghijklmnopqrstuvwxyz]/i;
  277.  
  278. //Remove space from the input FSA
  279. fsa = dojoLang.trim(fsa);
  280.  
  281. //Detect if the user has entered the postal code in correct format
  282. if (fsa) {
  283. var result = fsa.match(regexp);
  284. return (result && result.length === 1);
  285. }
  286. return false;
  287. }
  288.  
  289. this.button.on("click", dojoLang.hitch(this, function () {
  290. var postalCode = this.postalCodeTextbox.get("value");
  291. if (validatePostalCode(postalCode)) {
  292. var postalConfig = config.quickzoom.postalCode,
  293. query = new Query();
  294. query.where = UtilMisc.getWhereClause(postalConfig.id, postalCode);
  295. changeExtent(postalConfig.url, query);
  296. } else {
  297. console.log("invalid postal code!");
  298. that._setError("invalid postal code");
  299. }
  300. }));
  301.  
  302. var whereNode = dom.byId(where);
  303. domConstruct.place(this.form.domNode, whereNode, "replace");
  304. }
  305. });
  306. });