qd30map-cesium.js 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770
  1. //Declaring global variables representing DOM elements which will be used throughout the application
  2. var mapDom = document.getElementById('qd30map-cesium');
  3. var placeholderDom = document.getElementById('loading-placeholder');
  4. var navContainer = document.getElementById('nav-ui-wrap');
  5. var navParent = document.getElementById('nav-arrows');
  6. var navReset = document.getElementById('nav-reset');
  7. var mapSearchBtn = document.getElementById('map-search');
  8. var navElements = navParent.getElementsByTagName('button');
  9. var searchForm = document.getElementById('map-ui-form');
  10. var appContainer = document.getElementById('map-search');
  11. //These will represent the coordinates of the App and Form, which will be used in centering functions (this is
  12. //especially helpful for mobile use
  13. var appX = null;
  14. var appY = null;
  15. var formX= null;
  16. var formY = null;
  17. navElements[0].tabIndex = 3;
  18. navElements[1].tabIndex = 4;
  19. navElements[2].tabIndex = 5;
  20. navElements[3].tabIndex = 6;
  21. navReset.tabIndex = 7;
  22. mapSearchBtn.tabIndex = 8;
  23. //Iterate over the navigation elements, add listener and add the appropriate camera function
  24. for (var i=0; i < navElements.length; i++) {
  25. navElements[i].addEventListener('mousedown', function(param) {
  26. switch(param.target.id) {
  27. case 'nav-up':
  28. camera.moveUp(100);
  29. break;
  30. case 'nav-down':
  31. camera.moveDown(100);
  32. break;
  33. case 'nav-left':
  34. camera.moveLeft(100);
  35. break;
  36. case 'nav-right':
  37. camera.moveRight(100);
  38. break;
  39. }
  40. });
  41. navElements[i].addEventListener('keydown', function(param) {
  42. switch(param.target.id) {
  43. case 'nav-up':
  44. if (param.keyCode == 13) {
  45. camera.moveUp(100);
  46. }
  47. break;
  48. case 'nav-down':
  49. if (param.keyCode == 13) {
  50. camera.moveDown(100);
  51. }
  52. break;
  53. case 'nav-left':
  54. if (param.keyCode == 13) {
  55. camera.moveLeft(100);
  56. }
  57. break;
  58. case 'nav-right':
  59. if (param.keyCode == 13) {
  60. camera.moveRight(100);
  61. }
  62. break;
  63. }
  64. });
  65. //Reset the view
  66. navReset.onclick = function(param) {
  67. gotoQD30();
  68. };
  69. navReset.onkeyup = function(param) {
  70. if (param.keyCode == 13) {
  71. gotoQD30();
  72. }
  73. }
  74. }
  75. //Center the form -> especially for mobile users, as the map will take up the entire screen
  76. mapSearchBtn.onclick = function() {
  77. centerForm();
  78. };
  79. mapSearchBtn.onkeydown = function(param) {
  80. if (param.keyCode == 13) {
  81. centerForm();
  82. }
  83. };
  84. var zoomParent = document.getElementById('zoom');
  85. var zoomElements = zoomParent.getElementsByTagName('button');
  86. zoomElements[0].tabIndex = 1;
  87. zoomElements[1].tabIndex = 2;
  88. for (var a=0; a < zoomElements.length; a++) {
  89. zoomElements[a].addEventListener('mousedown', function(param) {
  90. var y = camera.position.y;
  91. switch(param.target.id) {
  92. case 'zoom-in':
  93. camera.moveForward(250);
  94. break;
  95. case 'zoom-out':
  96. camera.moveBackward(250);
  97. break;
  98. }
  99. });
  100. zoomElements[a].addEventListener('keydown', function(param) {
  101. var y = camera.position.y;
  102. switch(param.target.id) {
  103. case 'zoom-in':
  104. if (param.keyCode == 13) {
  105. camera.moveForward(100);
  106. }
  107. break;
  108. case 'zoom-out':
  109. if (param.keyCode == 13) {
  110. camera.moveBackward(100);
  111. }
  112. break;
  113. }
  114. });
  115. }
  116. //To check version
  117. // console.log(Cesium.VERSION);
  118. Cesium.BingMapsApi.defaultKey = 'mqrOOSbN2SMpDuWhHx0W~HVi_IW2A0UwRAV1xLIZuTQ~AnSezO-xLZ4_s1rf8ydB6Wf0aRdKtw_znPffJQ9qKbntyOPWFvitPVAvxT0v6dib';
  119. //Instantiate the viewer. This is the primary object from which most functionality is derived
  120. var viewer = new Cesium.Viewer('qd30map-cesium', {
  121. animation : false,
  122. homeButton : false,
  123. vrButton : false,
  124. infoBox : true,
  125. geocoder : false,
  126. sceneModePicker: false,
  127. selectionIndicator: true,
  128. timeline : false,
  129. navigationHelpButton : false,
  130. navigationInstructionsInitiallyVisible: false,
  131. scene3DOnly : false,
  132. shadows : true,
  133. terrainShadows : true,
  134. baseLayerPicker : false
  135. });
  136. var camera = viewer.camera;
  137. var scene = viewer.scene;
  138. var ellipsoid = scene.globe.ellipsoid;
  139. var canvas = viewer.canvas;
  140. var layers = viewer.scene.imageryLayers;
  141. var globe = viewer.scene.globe;
  142. //Reset the globe's imagery layers and begin with a light grey base
  143. globe.imageryLayers.removeAll();
  144. globe.baseColor = Cesium.Color.fromCssColorString('#f3f3f3');
  145. //Add a map tiling service over top of the base layer, to show the city street details
  146. var tonerLayer = layers.addImageryProvider(Cesium.createOpenStreetMapImageryProvider({
  147. url : 'http://tile.stamen.com/toner/'
  148. }));
  149. tonerLayer.alpha = 0.2;
  150. // var terrainProvider = new Cesium.CesiumTerrainProvider({
  151. // url : '//assets.agi.com/stk-terrain/world',
  152. // requestVertexNormals: true
  153. // });
  154. //Remove tab index from the canvas, as this won't be necessary for keyboard only users
  155. canvas.setAttribute('tabindex', -1);
  156. var reset = document.getElementById('nav-reset');
  157. reset.addEventListener('click', function() {
  158. gotoQD30();
  159. });
  160. //Load the datasource as a promise which triggers the "then" function upon loading
  161. var dataSource = Cesium.GeoJsonDataSource.load('/sites/quartierdix30/files/geojson/quartier_en.json').then(function(data) {
  162. viewer.dataSources.add(data);
  163. gotoQD30();
  164. //entities.values provides the entities as individual features, as found in the geoJson file. Iterate over these and
  165. //endow them with the appropriate properties for display
  166. var entities = data.entities.values;
  167. for (var i = 0; i < entities.length; i++) {
  168. var entity = entities[i];
  169. //Buildings should have a black outline by default
  170. entity.polygon.outline = true;
  171. entity.polygon.outlineWidth = 1.2;
  172. entity.polygon.outlineColor = Cesium.Color.BLACK;
  173. //use extrudedHeight to stretch buildings to their appropriate height, as per their level property (this is
  174. //adjustable for each node)
  175. if (entity.properties.hasOwnProperty('level')) {
  176. entity.polygon.extrudedHeight = 0.5 * (entity.properties.level * 10);
  177. }
  178. //The value of colour for each feature is dependent on which sector the entity has been assigned to.
  179. //Boutique Node is paired with a Zone Node, and Zone Nodes are organized by Sector, which is how they derive their colour.
  180. //The value found in the json is added dynamically everytime the map data is rebuilt
  181. if (entity.properties.hasOwnProperty("colour")) {
  182. if (entity.properties.hasOwnProperty("event")) {
  183. //If the boutique has a special event, we use the StripeMaterialProperty to make it stand out
  184. entity.polygon.material = new Cesium.StripeMaterialProperty({
  185. evenColor : Cesium.Color.fromCssColorString(entity.properties.colour),
  186. oddColor : Cesium.Color.BLACK,
  187. repeat : 10
  188. });
  189. } else {
  190. entity.polygon.material = Cesium.Color.fromCssColorString(entity.properties.colour);
  191. }
  192. }
  193. //The description property provides all of the markup for the infobox which pops up when a boutique is selected
  194. if (entity.properties.hasOwnProperty("description")) {
  195. entity.description = '<div>' + entity.properties.description
  196. + '</div>';
  197. }
  198. }
  199. });
  200. //This function takes us to the default view of the map
  201. function gotoQD30() {
  202. var pos = Cesium.Ellipsoid.WGS84.cartesianToCartographic(new Cesium.Cartesian3(
  203. 1278149.1996018, -4297423.8092246,
  204. 4522591.15943));
  205. pos.latitude = Cesium.Math.toDegrees(pos.latitude);
  206. pos.longitude = Cesium.Math.toDegrees(pos.longitude);
  207. viewer.camera.flyTo({
  208. destination : Cesium.Cartesian3.fromDegrees(pos.longitude, pos.latitude, pos.height),
  209. orientation : {
  210. heading : 5.51747,
  211. pitch : -1.419427,
  212. roll : 6.2675
  213. },
  214. duration: 0.5
  215. });
  216. }
  217. //Not currently used
  218. function clearLayers() {
  219. globe.imageryLayers.removeAll();
  220. globe.baseColor = new Cesium.Color(0, 0, 0.5, 1);
  221. }
  222. //Not currently used
  223. function customLayer() {
  224. globe.imageryLayers.removeAll();
  225. globe.baseColor = Cesium.Color.fromCssColorString('#f3f3f3');
  226. var tonerLayer = layers.addImageryProvider(Cesium.createOpenStreetMapImageryProvider({
  227. url : 'http://tile.stamen.com/toner/'
  228. }));
  229. tonerLayer.alpha = 0.2;
  230. }
  231. //Not currently used
  232. function highlight(chosenColour) {
  233. for (var j = 0; j < viewer.dataSources.get(0).entities.values.length; j++) {
  234. var entity = viewer.dataSources.get(0).entities.values[j];
  235. if (entity.properties.hasOwnProperty('colour')) {
  236. if (entity.properties.colour.toLowerCase() == chosenColour.toLowerCase()) {
  237. entity.polygon.material = Cesium.Color.BLACK;
  238. entity.polygon.outlineWidth = 2.0;
  239. }
  240. }
  241. }
  242. }
  243. //Resets all boutiques to their default initial colour as per their colour and event properties
  244. function colourReset() {
  245. // for (z = viewer.entities.values.length -1; z >= 0; z--) {
  246. // if (viewer.entities.values[z].name.substring(0, 16) == 'selectionOutline') {
  247. // viewer.entities.remove(viewer.entities.values[z]);
  248. // }
  249. // }
  250. for (var j = 0; j < viewer.dataSources.get(0).entities.values.length; j++) {
  251. var entity = viewer.dataSources.get(0).entities.values[j];
  252. if (entity.properties.hasOwnProperty('colour')) {
  253. if (entity.properties.hasOwnProperty("event")) {
  254. entity.polygon.material = new Cesium.StripeMaterialProperty({
  255. evenColor : Cesium.Color.fromCssColorString(entity.properties.colour),
  256. oddColor : Cesium.Color.BLACK,
  257. repeat : 10
  258. });
  259. } else {
  260. entity.polygon.material = Cesium.Color.fromCssColorString(entity.properties.colour);
  261. entity.polygon.outlineWidth = 1.2;
  262. entity.polygon.outlineColor = Cesium.Color.BLACK;
  263. }
  264. }
  265. }
  266. }
  267. //Centers the view over the map canvas
  268. function centerApp() {
  269. window.scrollTo(appX, appY);
  270. }
  271. //Centers the view over the form
  272. function centerForm() {
  273. window.scrollTo(formX, formY);
  274. }
  275. //Helper function to determine position of an element
  276. function getPosition(el) {
  277. var xPos = 0;
  278. var yPos = 0;
  279. while (el) {
  280. if (el.tagName == "BODY") {
  281. // deal with browser quirks with body/window/document and page scroll
  282. var xScroll = el.scrollLeft || document.documentElement.scrollLeft;
  283. var yScroll = el.scrollTop || document.documentElement.scrollTop;
  284. xPos += (el.offsetLeft - xScroll + el.clientLeft);
  285. yPos += (el.offsetTop - yScroll + el.clientTop);
  286. } else {
  287. // for all other non-BODY elements
  288. xPos += (el.offsetLeft - el.scrollLeft + el.clientLeft);
  289. yPos += (el.offsetTop - el.scrollTop + el.clientTop);
  290. }
  291. el = el.offsetParent;
  292. }
  293. return {
  294. x: xPos,
  295. y: yPos
  296. };
  297. }
  298. //Not currently used
  299. function getOffset( el ) {
  300. var _x = 0;
  301. var _y = 0;
  302. while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
  303. _x += el.offsetLeft - el.scrollLeft;
  304. _y += el.offsetTop - el.scrollTop;
  305. el = el.offsetParent;
  306. }
  307. return { top: _y, left: _x };
  308. }
  309. //Determines whether or not an element is visible
  310. function domIsVisible(element) {
  311. var rect = element.getBoundingClientRect();
  312. var html = document.documentElement;
  313. return (
  314. rect.top >= 0 &&
  315. rect.left >= 0 &&
  316. rect.bottom <= (window.innerHeight || html.clientHeight) &&
  317. rect.right <= (window.innerWidth || html.clientWidth)
  318. );
  319. }
  320. window.onload = function() {
  321. mapDom.style.display = 'inline-block';
  322. searchForm.style.display = 'inline-block';
  323. navContainer.style.display = 'block';
  324. placeholderDom.style.display = 'none';
  325. //Set the form and canvas X and Y values after loading the window
  326. var formPosition = getPosition(searchForm);
  327. formX = formPosition.x;
  328. formY = formPosition.y;
  329. var appPosition = getPosition(appContainer);
  330. appX = appPosition.x;
  331. appY = appPosition.y ;
  332. centerApp();
  333. // var formAnchorTags = Array.from(searchForm.querySelectorAll("a"));
  334. // formAnchorTags.forEach(function(anchor) {
  335. // if (!anchor.children[0].classList.contains('hidden-nid')) {
  336. // console.dir(anchor.parentElement);
  337. // console.dir(anchor.parentNode);
  338. // }
  339. // if (anchor.parentElement.nodeName == 'SUMMARY') {
  340. // anchor.parentElement.tabIndex = -1;
  341. // }
  342. //
  343. // });
  344. //Reload the stylesheet for the infobox, as it is an iframe and its styling needs to be enforced after the window has loaded
  345. var frame = viewer.infoBox.frame;
  346. var cssLink = frame.contentDocument.createElement('link');
  347. cssLink.href = Cesium.buildModuleUrl('../../../../css/qd30map.css');
  348. cssLink.rel = 'stylesheet';
  349. cssLink.type = 'text/css';
  350. frame.contentDocument.head.appendChild(cssLink);
  351. //Remove tabindex from elements within the header
  352. var header = document.getElementById('header');
  353. var headChilds = header.getElementsByTagName('*');
  354. for (var c = 0; c < headChilds.length; c++) {
  355. headChilds[c].tabIndex = -1;
  356. }
  357. var qdTitle = document.getElementById('block-quartierdix30-page-title');
  358. var titleChilds = qdTitle.getElementsByTagName('*');
  359. for (var d = 0; d < titleChilds.length; d++) {
  360. titleChilds[d].tabIndex = -1;
  361. }
  362. //Set a variable to represent the primary datasource, which contains all of the geojson features
  363. var data = viewer.dataSources.get(0);
  364. var subBtn = document.getElementById('edit-mapsubmit');
  365. //Set a listener for the form's submit button, so submissions can be overridden
  366. subBtn.onclick = function(v) {
  367. v.preventDefault();
  368. handleSubmit();
  369. };
  370. //Set the same type of listener for the autocomplete field, whose submit logic must also be overridden
  371. var autoComplete = document.getElementById('ui-id-1');
  372. autoComplete.onclick = function() {
  373. handleSubmit();
  374. };
  375. autoComplete.onkeydown = function(param) {
  376. if (param.keyCode == 13) {
  377. handleSubmit();
  378. }
  379. };
  380. //Override selections based on clicking objects within the WebGL Canvas
  381. clickHandler = new Cesium.ScreenSpaceEventHandler(scene.canvas);
  382. clickHandler.setInputAction(function(movement) {
  383. var pickedObject = scene.pick(movement.position);
  384. if (Cesium.defined(pickedObject)) {
  385. for (var i = 0; i < data.entities.values.length; i++) {
  386. var entity = data.entities.values[i];
  387. if (entity.properties.nid != undefined) {
  388. if (entity.properties.nid == pickedObject.id.properties.nid) {
  389. exploreStore(entity, data);
  390. }
  391. }
  392. }
  393. }
  394. }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
  395. //Iterate over all the anchor elements in the form and setup appropriate click/keyboard listeners to select stores
  396. var anchorElements = searchForm.querySelectorAll('.form-item');
  397. for (var i = 0; i < anchorElements.length; i++) {
  398. anchorElements[i].tabIndex = 0;
  399. }
  400. for (var k=0; k < anchorElements.length; k++) {
  401. anchorElements[k].addEventListener('click', function(param) {
  402. for (var g=0; g < anchorElements.length; g++) {
  403. //Adding the selected-store class to the elements will cause them to appear highlighted within the form
  404. //We must first remove this from the previous selected element
  405. anchorElements[g].classList.remove('selected-store');
  406. }
  407. var choice = param.target.innerHTML.trim();
  408. for (var i = 0; i < data.entities.values.length; i++) {
  409. var entity = data.entities.values[i];
  410. if (entity.properties.nid != undefined) {
  411. var formNid = choice.substring(choice.indexOf('d">') + 3, choice.indexOf('</spa'));
  412. //Form selection occurs by comparing the nid value hidden within the form element with the nid property of the geojson feature
  413. if (formNid == entity.properties.nid) {
  414. if (entity.polygon.hierarchy._value.positions.length > 1) {
  415. selectStore(entity, data);
  416. } else {
  417. gotoQD30();
  418. }
  419. if (!domIsVisible(appContainer)) {
  420. centerApp();
  421. }
  422. this.classList.add('selected-store');
  423. }
  424. }
  425. }
  426. });
  427. //This logic is the same above, but for selection via keyboard
  428. anchorElements[k].addEventListener('keyup', function(param) {
  429. for (var g=0; g < anchorElements.length; g++) {
  430. anchorElements[g].classList.remove('selected-store');
  431. }
  432. if (param.keyCode == 13) {
  433. var choice = param.target.innerHTML.trim();
  434. for (var i = 0; i < data.entities.values.length; i++) {
  435. var entity = data.entities.values[i];
  436. if (entity.properties.nid != undefined) {
  437. var formNid = choice.substring(choice.indexOf('d">') + 3, choice.indexOf('</spa'));
  438. if (formNid == entity.properties.nid) {
  439. if (entity.polygon.hierarchy._value.positions.length > 1) {
  440. selectStore(entity, data);
  441. } else {
  442. gotoQD30();
  443. }
  444. if (!domIsVisible(appContainer)) {
  445. centerApp();
  446. }
  447. this.classList.add('selected-store');
  448. }
  449. }
  450. }
  451. }
  452. });
  453. }
  454. //This listener is used to highlight multiple boutiques by store type
  455. var types = Array.from(document.querySelectorAll('summary > a.details-title'));
  456. types.forEach(function(type) {
  457. type.addEventListener('click', function(param) {
  458. var text = param.srcElement.lastChild.textContent.toLowerCase();
  459. highlightByType(text, data);
  460. });
  461. });
  462. //this function is used to override form submission
  463. function handleSubmit() {
  464. var search = document.getElementById('edit-search');
  465. var formNid = search.value.substring(search.value.indexOf('(') + 1, search.value.indexOf(')'));
  466. var length = data.entities.values.length;
  467. for (var i = 0; i < length; i++) {
  468. var entity = data.entities.values[i];
  469. if (entity.properties.nid != undefined) {
  470. console.log(entity.properties.nid);
  471. console.log(formNid);
  472. if (formNid == entity.properties.nid) {
  473. if (entity.polygon.hierarchy._value.positions.length > 1) {
  474. selectStore(entity, data);
  475. } else {
  476. gotoQD30();
  477. }
  478. }
  479. }
  480. }
  481. }
  482. //The primary function for selecting a boutique: finds the average point between the feature's coordinates, flies the camera
  483. //at an appropriate angle, colours the building and sets the viewer.selectedEntity property to it, triggering the display
  484. //of the infobox popup
  485. function selectStore(entity, data) {
  486. var pCoords = entity.polygon.hierarchy.getValue(viewer.clock.currentTime);
  487. var avgPoint = Math.round(pCoords.positions.length / 2);
  488. var topPoint = 0;
  489. for (var xy = 0; xy < pCoords.positions.length - 1; xy++) {
  490. if (pCoords.positions[xy+1].x > pCoords.positions[xy].x) {
  491. topPoint = xy;
  492. }
  493. }
  494. var pos = Cesium.Ellipsoid.WGS84.cartesianToCartographic(pCoords.positions[avgPoint]);
  495. pos.latitude = Cesium.Math.toDegrees(pos.latitude);
  496. pos.longitude = Cesium.Math.toDegrees(pos.longitude);
  497. viewer.camera.flyTo({
  498. destination : Cesium.Cartesian3.fromDegrees(pos.longitude, pos.latitude, 500),
  499. orientation : {
  500. heading : 5.51747,
  501. pitch : -1.419427,
  502. roll : 6.2675
  503. },
  504. duration: 1
  505. });
  506. colourReset();
  507. //Previously, we wrapped highlighted buildings with a glowing polyline to improve contrast
  508. // viewer.entities.add({
  509. // name : 'selectionOutline',
  510. // polyline : {
  511. // positions : pCoords.positions,
  512. // width : 4,
  513. // material : new Cesium.PolylineGlowMaterialProperty({
  514. // glowPower : 0.5,
  515. // color : Cesium.Color.fromCssColorString('#008B8B'),
  516. // extrudedHeight : entity.polygon.extrudedHeight + 20
  517. // })
  518. // }
  519. // });
  520. entity.polygon.material = Cesium.Color.fromCssColorString('#000000');
  521. entity.polygon.material.outlineColor = Cesium.Color.fromCssColorString('#008B8B');
  522. entity.polygon.material.outlineWidth = 5;
  523. viewer.selectedEntity = entity;
  524. }
  525. //this function is similar to above, but used for clicking on the canvas. This preserves the zoom height which the user
  526. //is previously using, such as to avoid the confusion of changing height each time a building is clicked on
  527. function exploreStore(entity, data) {
  528. var pCoords = entity.polygon.hierarchy.getValue(viewer.clock.currentTime);
  529. var cartographic = new Cesium.Cartographic();
  530. var camHeight = ellipsoid.cartesianToCartographic(camera.position, cartographic).height;
  531. // var pos = Cesium.Ellipsoid.WGS84.cartesianToCartographic(pCoords.positions[0]);
  532. // pos.latitude = Cesium.Math.toDegrees(pos.latitude);
  533. // pos.longitude = Cesium.Math.toDegrees(pos.longitude);
  534. var avgPoint = Math.round(pCoords.positions.length / 2);
  535. var pos = Cesium.Ellipsoid.WGS84.cartesianToCartographic(pCoords.positions[avgPoint]);
  536. pos.latitude = Cesium.Math.toDegrees(pos.latitude);
  537. pos.longitude = Cesium.Math.toDegrees(pos.longitude);
  538. // var topPoint = 0;
  539. //
  540. // for (var xy = 0; xy < pCoords.positions.length - 1; xy++) {
  541. // if (pCoords.positions[xy+1].x > pCoords.positions[xy].x) {
  542. // topPoint = xy;
  543. // }
  544. // }
  545. viewer.camera.flyTo({
  546. destination : Cesium.Cartesian3.fromDegrees(pos.longitude, pos.latitude, camHeight),
  547. orientation : {
  548. heading : camera.heading
  549. },
  550. duration: 1
  551. });
  552. colourReset();
  553. // pCoords.positions.forEach(function(position) {
  554. // position.x += 0.33;
  555. // position.y += 0.33;
  556. // });
  557. // var selectionOutline = viewer.entities.add({
  558. // name : 'selectionOutline',
  559. // polyline : {
  560. // positions : pCoords.positions,
  561. // width : 4,
  562. // material : new Cesium.PolylineGlowMaterialProperty({
  563. // glowPower : 0.5,
  564. // color : Cesium.Color.fromCssColorString('#008B8B'),
  565. // extrudedHeight : entity.polygon.extrudedHeight + 20
  566. // })
  567. // }
  568. // });
  569. entity.polygon.material = Cesium.Color.fromCssColorString('#000000');
  570. entity.polygon.material.outlineColor = Cesium.Color.fromCssColorString('#008B8B');
  571. entity.polygon.material.outlineWidth = 5;
  572. viewer.selectedEntity = entity;
  573. }
  574. //This is the function used to highlight multiple boutiques based on their store type (restaurant, entertainment, "accepts gift cards", etc)
  575. function highlightByType(type, data) {
  576. colourReset();
  577. // for (z = 0; z < viewer.entities.values.length; z++) {
  578. // if (viewer.entities.values[z].name.substring(0, 16) == 'selectionOutline') {
  579. // viewer.entities.remove(viewer.entities.values[z]);
  580. // }
  581. // }
  582. for (var j = 0; j < data.entities.values.length; j++) {
  583. var entity = data.entities.values[j];
  584. if (entity.properties.hasOwnProperty('storetypes')) {
  585. if (entity.properties.storetypes.toLowerCase().match(type)) {
  586. var pCoords = entity.polygon.hierarchy.getValue(viewer.clock.currentTime);
  587. matched = true;
  588. entity.polygon.material = Cesium.Color.fromCssColorString('#000000');
  589. entity.polygon.material.outlineColor = Cesium.Color.fromCssColorString('#008B8B');
  590. entity.polygon.material.outlineWidth = 5;
  591. // viewer.entities.add({
  592. // name : 'selectionOutline',
  593. // polyline : {
  594. // positions : pCoords.positions,
  595. // width : 4,
  596. // material : new Cesium.PolylineGlowMaterialProperty({
  597. // glowPower : 0.5,
  598. // color : Cesium.Color.fromCssColorString('#008B8B'),
  599. // extrudedHeight : entity.polygon.extrudedHeight + 20
  600. // })
  601. // }
  602. // });
  603. }
  604. }
  605. }
  606. }
  607. //Logic to close all non-target details elements
  608. //This was important to keep the use of the form from becoming confusing, as there are many collapsible details elements
  609. var details = Array.from(document.querySelectorAll("details"));
  610. details.forEach(function(detail) {
  611. if (detail.constructor.name === 'HTMLDetailsElement') {
  612. detail.addEventListener('click', function() {
  613. for (f = 0; f < details.length; f++) {
  614. details[f].classList.remove('selected-detail');
  615. }
  616. this.classList.add('selected-detail');
  617. subDet = this.querySelectorAll('details');
  618. for (g = 0; g < subDet.length; g++) {subDet[g].classList.add('selected-detail');}
  619. closeOthers(this);
  620. });
  621. }
  622. });
  623. //helper function used in the above listeners
  624. function closeOthers(elem) {
  625. for (var i = 0; i < details.length; i++) {
  626. if (!details[i].contains(elem)) {
  627. var selectedFound = false;
  628. var elems = details[i].querySelectorAll('*');
  629. for (z = 0; z < elems.length; z++) {
  630. if (elems[z].classList.contains('selected-detail') || elems[z].classList.contains('selected-store')) {
  631. selectedFound = true;
  632. }
  633. }
  634. if (selectedFound == false) {
  635. details[i].removeAttribute('open');
  636. }
  637. } else {
  638. details[i].setAttribute('open', 'open');
  639. }
  640. }
  641. }
  642. //This helper function is no longer used, but was helpful during development to find the visible coordinates on the canvas
  643. function getExtentView() {
  644. var cl2 = new Cesium.Cartesian2(0, 0);
  645. var leftTop = viewer.scene.camera.pickEllipsoid(cl2, ellipsoid);
  646. cr2 = new Cesium.Cartesian2(viewer.scene.canvas.width, viewer.scene.canvas.height);
  647. var rightDown = viewer.scene.camera.pickEllipsoid(cr2, ellipsoid);
  648. if (leftTop != null && rightDown != null) {
  649. leftTop = ellipsoid.cartesianToCartographic(leftTop);
  650. rightDown = ellipsoid.cartesianToCartographic(rightDown);
  651. return new Cesium.Rectangle(leftTop.longitude, rightDown.latitude, rightDown.longitude, leftTop.latitude);
  652. } else {
  653. console.log("Sky is visible");
  654. return null;
  655. }
  656. }
  657. };
  658. //It is important to update the values of the form and app positions upon window resize
  659. window.onresize = function() {
  660. var formPosition = getPosition(searchForm);
  661. formX = formPosition.x;
  662. formY = formPosition.y;
  663. var appPosition = getPosition(appContainer);
  664. appX = appPosition.x;
  665. appY = appPosition.y;
  666. };
  667. //This jQuery code was being used for the map legend
  668. (function($, Drupal) {
  669. Drupal.behaviors.qd30map = {
  670. attach: function(context, settings) {
  671. $('.closemenu').on('mouseenter', function() {
  672. $(this).find('span').show();
  673. }).on('mouseleave', function() {
  674. $(this).find('span').hide();
  675. }).on('click tap', function() {
  676. $(this).parent().parent().children('div').fadeOut(150);
  677. $(this).parent().parent().delay(50).animate({width: 'toggle'}, 300);
  678. if ($(this).parent().hasClass('closeinfo')) {
  679. infoOpen = false;
  680. $('.selected').removeClass('selected');
  681. }
  682. });
  683. $('#open-legend').on('click tap', function() {
  684. $('.map-legend-info').animate({width:'toggle'}, 300);
  685. $('.map-legend-info > div').delay(150).fadeIn(150);
  686. });
  687. }}})(jQuery, Drupal);
  688. //Helper function not currently being used
  689. function fade(element) {
  690. var op = 1; // initial opacity
  691. var timer = setInterval(function() {
  692. if (op <= 0.1) {
  693. clearInterval(timer);
  694. element.style.display = 'none';
  695. }
  696. element.style.opacity = op;
  697. element.style.filter = 'alpha(opacity=' + op * 100 + ")";
  698. op -= op * 0.1;
  699. }, 150);
  700. }