Changeset 326 in tmcsimulator for trunk/webapps/cptms.html
- Timestamp:
- 03/17/2019 09:57:48 AM (7 years ago)
- File:
-
- 1 edited
-
trunk/webapps/cptms.html (modified) (16 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/webapps/cptms.html
r325 r326 6 6 <meta http-equiv="Content-Type" content="text/html;charset=utf-8"> 7 7 <title>CPTMS Map v0.6.2</title> 8 <style> 8 <!-- Styles --> 9 <link rel="stylesheet" href=""> 10 <style> 9 11 @font-face { 10 12 font-family: Scoreboard; 11 src: url(' scoreboard.ttf');13 src: url('js/scoreboard.ttf'); 12 14 } 13 15 /* Set the size of the div element that contains the map */ … … 22 24 padding: 0; 23 25 } 26 /* A line of the CMS message display */ 24 27 textarea { 25 28 height: 33px; … … 31 34 color: yellow; 32 35 } 36 /* Field for entering CMS Message line */ 33 37 input { 34 38 border: thin solid #333; … … 49 53 width: 400px; 50 54 } 51 52 55 #search-input:focus { 53 56 border-color: #E6E6FA; … … 79 82 float: left; 80 83 } 84 /* CMS Dialog button panel */ 81 85 #buttonPanel { 82 86 height: 122px; … … 90 94 position: relative;\ 91 95 } 96 /* CMS Edit Message Dialog */ 92 97 #dialog { 93 98 position: absolute; … … 125 130 <body> 126 131 <!-- 132 Version 6.3 decomposed into modules for each layer 127 133 Version 6.2 puts cctv and cms and vds in separate data layers. 128 134 Version 6.1 Puts cms messages in json formatted file. Polls for updates. … … 146 152 <!-- The text area input for the Search Box --> 147 153 <input id="search-input" class="controls" type="text" placeholder="Search Box"> 148 <!--The div element where the map appears -->154 <!--The div element where the map appears. --> 149 155 <div id="mapdiv"></div> 150 <!-- The div element for the popup dialog -->156 <!-- The div element for the popup dialog. Best results when placed here. --> 151 157 <div id="dialog" style="display:none;"> 152 158 <span class="close">☒</span> <!-- close button symbol --> … … 176 182 <button id="cmsButton" class="unstyled-button"><img id="cmsBtnImg" src="images/btnReady_CMS.png"></button> 177 183 <button id="vdsButton" class="unstyled-button"><img id="vdsBtnImg" src="images/btnDepressed_VDS.png"></button> 178 179 184 <script> 180 //TODO:181 // Add phase 2 to messages.182 // cms set visible gets undefined error after last icon is processed.183 185 184 186 // a global variable for the google map … … 188 190 // a global variable to hold locations of marked search places 189 191 var placePins = []; 192 // the cms message local dictionary 193 var messageDict = {}; 190 194 // Constant for map center location: The John Wayne Airport 191 195 //var centerPoint = {lat: 33.687228, lng: -117.872148}; … … 208 212 209 213 var kMapPointsFile = "highways.json"; // dynamic json data file created by CADserver 210 var kMapStartupFile = " highways_startup.json"; // initial (static) highways file used once at startup214 var kMapStartupFile = "data_layers/highways_startup.json"; // initial (static) highways file used once at startup 211 215 var kCMSfile = "data_layers/cms_locations_D12.gjson"; // CMS locations 212 216 var kCCTVfile = "data_layers/cctv_locations_D12.gjson"; // CCTV locations … … 218 222 var vdsIconYellow = "images/circle-yellow.png" 219 223 var vdsIconRed = "images/circle-red.png" 220 var cms_info;221 var messageDict = {};222 224 var cms_showing = false; 223 225 var vds_showing = true; … … 225 227 var cctv_infowindow; // We create just a single instance of info window. 226 228 227 // Build a solid colored icon to use instead of the classic pin 228 // Use a diamond on N and E directions, circle on S and W directions 229 function dotSymbol(color) //,direction) 230 { 231 // var circle = google.maps.SymbolPath.CIRCLE; 232 // var diamond = 'M -1,0 0,-1 1,0 0,1 z'; 233 // var myShape = circle; 234 var iconPath = vdsIconGreen; 235 if (color == 'red') 236 { 237 iconPath = vdsIconRed; 238 } 239 else if (color == 'yellow') 240 { 241 iconPath = vdsIconYellow; 242 } 243 return { 244 // path: iconPath, 245 // icon: 246 // { 247 url: iconPath, 248 anchor: new google.maps.Point(6, 6) 249 // }; 250 // anchor: new google.maps.Point(6, 6), 251 // scale: 5, 252 // strokeColor: "black", // the border color 253 // strokeWeight: 1, // the border thickness 254 // fillColor: color, 255 // fillOpacity: 1.0 256 }; 257 } 258 259 // Load the map data from a json file and style all the points 260 function loadVDSlayer() 261 { 262 // Load the static map data and call saveCoords when done 263 map.data.loadGeoJson(kMapStartupFile, null, saveCoords) 264 // var d = new Date(); 265 // var start = d.getTime(); 266 // Style the map data by applying the desired properties to each feature (marker) 267 // The function will be called every time a feature's properties are updated. 268 map.data.setStyle(function(feature) 269 { 270 // Get the postmile id 271 var name = feature.getId(); 272 // Get the desired color value 273 var ptColor = feature.getProperty("color"); 274 var street = feature.getProperty("street"); 275 // Build the marker 276 var iconSymbol = dotSymbol(ptColor); 277 // return the StyleOptions 278 return { 279 icon: iconSymbol, 280 /* icon: 281 { 282 url: vdsIconGreen, 283 anchor: new google.maps.Point(6, 6) 284 }, */ 285 title: name + " @" + street, // set rollover text 286 // set zIndex for slowed traffic to a higher value so they overlap 287 zIndex: colorZvalues[ptColor] 288 }; 289 }); 290 } 291 // callback when load GeoJson completes 292 // save each feature's Point as the original coordinates for later reference 293 function saveCoords(features) 294 { 295 // Iterate over all the features in the map 296 features.forEach(function(feature) 297 { 298 var pt = feature.getGeometry().get(); 299 vds_coords[feature.getId()] = pt; // save the Point in a dictionary 300 }); 301 // update the dot colors from the dynamic json data 302 updateVDSlayer(); 303 // go adjust the marker coordinates so dots don't overlap 304 adjustCoords(calcDistanceFactor()); 305 } 306 307 // magic formula controls distance between dots proportionate to zoom factor 308 function calcDistanceFactor() 309 { 310 // 15 is maximum zoom, the point at which no adjustment is needed 311 return (.0005 * (15 - map.getZoom())); 312 } 313 314 // Adjust the coordinates of dots so they appear side-by-side 315 // The perpendicular vector for each dot has been provided, 316 // so we just need to multiply by a scaling factor (adjAmount) 317 // @param adjAmount amount by which to adjust coordinate 318 function adjustCoords(adjAmount) 319 { 320 // Adjust the NB points a slight amount 321 map.data.forEach(function(feature) 322 { 323 // get the name of the current feature 324 var name = feature.getId(); 325 // lookup the original coordinates for this feature 326 var coords = vds_coords[name]; 327 328 //retrieve the perpendicular vector (precomputed) 329 var perpx = feature.getProperty("perpx") 330 var perpy = feature.getProperty("perpy") 331 // Make adjustment and save it 332 var myLat = coords.lat() + perpy * adjAmount 333 var myLong = coords.lng() + perpx * adjAmount 334 feature.setGeometry( 335 { 336 lat: myLat, 337 lng: myLong 338 }); 339 }); 340 } 341 342 // update the color (as needed) for a given marker 343 function updateMarker(marker) 344 { 345 target = marker.id; 346 newColor = marker.properties.color; 347 // see if new color is different than current color 348 currentFeature = map.data.getFeatureById(target); 349 currentColor = currentFeature.getProperty("color"); 350 // if a new color is desired then assign it to the feature's color property 351 if (currentColor != newColor) 352 { 353 currentFeature.setProperty("color", newColor); 354 // set zIndex for slowed traffic to a higher value so they overlap 355 currentFeature.setProperty("zIndex", colorZvalues[newColor]); 356 } 357 } 358 359 // Load the dynamic json file for highways, etc. 360 // Ref: https://codepen.io/KryptoniteDove/post/load-json-file-locally-using-pure-javascript 361 function loadJSON(inFile, callback) 362 { 363 var xobj = new XMLHttpRequest(); 364 xobj.overrideMimeType("application/json"); 365 xobj.open('GET', inFile, true); 366 xobj.onreadystatechange = function() 367 { 368 if (xobj.readyState == 4 && xobj.status == "200") 369 { 370 callback(xobj.responseText); 371 } 372 }; 373 // We want ajax to ignore any cached responses 374 xobj.setRequestHeader('If-Modified-Since', 'Sat, 01 Jan 2000 01:01:01 GMT') 375 xobj.send(null); 376 } 377 // Load the highways dynamic json file and update the map 378 function updateVDSlayer() 379 { 380 var parsed_JSON; 381 loadJSON(kMapPointsFile, function(response) 382 { 383 // Parse JSON string into object 384 parsed_JSON = JSON.parse(response); 385 // Process each new marker - lookup in current map 386 parsed_JSON.features.forEach(updateMarker); 387 }); 388 } 389 390 // Initialize the center button (to re-center the map) 391 function initCenter() 392 { 393 var centerBtnDiv = document.getElementById('ctrButton'); 394 map.controls[google.maps.ControlPosition.RIGHT_CENTER].push(centerBtnDiv) 395 centerBtnDiv.title = 'Click to recenter the map'; 396 397 // Setup the click event listeners: reset center location and zoom factor 398 centerBtnDiv.addEventListener('click', function() 399 { 400 map.setCenter(centerPoint); 401 map.setZoom(initZoom); 402 clearPlacePins(); 403 }); 404 } 405 406 // Initialize the search box and listener 407 function initSearch() 408 { 409 // Create the search box and link it to the UI element. 410 var input = document.getElementById('search-input'); 411 var searchBox = new google.maps.places.SearchBox(input); 412 map.controls[google.maps.ControlPosition.TOP_LEFT].push(input); 413 414 // Bias the SearchBox results towards current map's viewport. 415 map.addListener('bounds_changed', function() 416 { 417 searchBox.setBounds(map.getBounds()); 418 }); 419 420 // Listen for the event fired when the user selects a prediction and retrieve 421 // more details for that place. 422 searchBox.addListener('places_changed', function() 423 { 424 var places = searchBox.getPlaces(); 425 426 if (places.length == 0) 427 { 428 return; 429 } 430 431 clearPlacePins(); 432 433 // Create a bounding region to include the search result places 434 var bounds = new google.maps.LatLngBounds(); 435 // For each place, get the icon, name and location. 436 // There may be multiple search results 437 places.forEach(function(place) 438 { 439 if (!place.geometry) 440 { 441 console.log("Returned place contains no geometry"); 442 return; 443 } 444 445 // Create a marker for each place. 446 placeMarker = new google.maps.Marker( 447 { 448 map: map, 449 title: place.name, 450 position: place.geometry.location 451 }) 452 453 // Click on the marker to remove it from the display 454 placeMarker.addListener('click', function() 455 { 456 placeMarker.setMap(null); 457 }); 458 459 // Add this marker to the collection of current markers 460 placePins.push(placeMarker); 461 462 // Create a bounding region to include this place 463 if (place.geometry.viewport) 464 { 465 // Only geocodes have viewport. 466 bounds.union(place.geometry.viewport); 467 } 468 else 469 { 470 bounds.extend(place.geometry.location); 471 } 472 }); 473 // This will pan and zoom to the area around the marker 474 //map.fitBounds(bounds); 475 // This will center the map on the new marker(s) but not zoom 476 map.setCenter(bounds.getCenter()); 477 }); 478 } 479 480 // Remove any place pins from a previous search 481 function clearPlacePins() 482 { 483 placePins.forEach(function(marker) 484 { 485 marker.setMap(null); 486 }); 487 placePins = []; 488 } 229 489 230 // Initialize the view/hide buttons 490 231 function initLayerButtons() 491 232 { 492 var cctvBtnDiv = document.getElementById('cctvButton'); 493 map.controls[google.maps.ControlPosition.LEFT_BOTTOM].push(cctvBtnDiv) 494 cctvBtnDiv.title = 'Click to toggle cctv view'; 495 // Setup the click event listeners to toggle icon display 496 cctvBtnDiv.addEventListener('click', function() 497 { 498 cctv_showing = !cctv_showing; 499 // reveal or hide all the icons 500 cctvLayer.forEach(function(feature) 501 { 502 cctvLayer.overrideStyle(feature, 503 { 504 visible: cctv_showing 505 }); 506 }); 507 // Determine which button image to show 508 if (cctv_showing) 509 { 510 pic = "images/btnDepressed_CCTV.png" 511 } 512 else 513 { 514 pic = "images/btnReady_CCTV.png" 515 } 516 document.getElementById('cctvBtnImg').src = pic; 517 }); 518 519 var cmsBtnDiv = document.getElementById('cmsButton'); 520 map.controls[google.maps.ControlPosition.LEFT_BOTTOM].push(cmsBtnDiv) 521 cmsBtnDiv.title = 'Click to toggle cms view'; 522 // Setup the click event listeners to toggle icon display 523 cmsBtnDiv.addEventListener('click', function() 524 { 525 cms_showing = !cms_showing; 526 // Determine which button image to show 527 if (cms_showing) 528 { 529 pic = "images/btnDepressed_CMS.png" 530 // It's nice when icons become visible that the messages have been refreshed. 531 loadAllMessages(); 532 } 533 else 534 { 535 pic = "images/btnReady_CMS.png" 536 } 537 document.getElementById('cmsBtnImg').src = pic; 538 // reveal or hide all the icons 539 cmsLayer.forEach(function(feature) 540 { 541 cmsLayer.overrideStyle(feature, 542 { 543 visible: cms_showing 544 }); 545 }); 546 }); 547 548 var vdsBtnDiv = document.getElementById('vdsButton'); 549 map.controls[google.maps.ControlPosition.LEFT_BOTTOM].push(vdsBtnDiv) 550 vdsBtnDiv.title = 'Click to toggle vds view'; 551 552 // Setup the click event listeners to toggle icon display 553 vdsBtnDiv.addEventListener('click', function() 554 { 555 vds_showing = !vds_showing; 556 // reveal or hide all the dots 557 map.data.forEach(function(feature) 558 { 559 map.data.overrideStyle(feature, 560 { 561 visible: vds_showing 562 }); 563 }); 564 // Determine which button image to show 565 if (vds_showing) 566 { 567 pic = "images/btnDepressed_VDS.png" 568 } 569 else 570 { 571 pic = "images/btnReady_VDS.png" 572 } 573 document.getElementById('vdsBtnImg').src = pic; 574 }); 575 } 576 577 function loadCMSlayer() 578 { 579 cmsLayer = new google.maps.Data(); 580 cmsLayer.setMap(map); 581 cmsLayer.loadGeoJson(kCMSfile); 582 cmsLayer.setStyle(function(feature) 583 { 584 // return the StyleOptions 585 return { 586 icon: yellowFlag, 587 title: feature.getId()+ " " +feature.getProperty("location"), 588 visible: false 589 }; 590 }); 591 592 cmsLayer.addListener('click', function(event) 593 { 594 var dialog = document.getElementById('dialog'); 595 // Note: If the dialog is already being displayed when someone else 596 // updates the message, it won't be reflected in the dialog, until 597 // you close and reopen it. 598 dialog.style.display = 'block'; 599 cmsID = event.feature.getId(); 600 // Assign to the hidden field 601 document.getElementById('cmsID').value = cmsID; 602 showMessage(cmsID); // note: this is async 603 document.getElementById('cms-info-label').innerHTML = "CMS ID: " + 604 cmsID + " LOCATION: " + event.feature.getProperty("location"); 605 // clear input fields 606 document.getElementById('msgcontent1').value = ""; 607 document.getElementById('msgcontent2').value = ""; 608 document.getElementById('msgcontent3').value = ""; 609 document.getElementById('msgcontent1').focus(); 610 var span = document.getElementsByClassName("close")[0] 611 // When the user clicks on <span> (x), close the modal 612 span.onclick = function() 613 { 614 handleDialogClose(); 615 } 616 }); 617 } 618 function makecctvIcon(nearVDS) 619 { 620 var imgIcon = cctvIcon 621 if ((typeof map.data.getFeatureById(nearVDS)) == "undefined") 622 { 623 imgIcon = cctvIconWhite; 624 } 625 return imgIcon 626 } 627 function loadCCTVlayer() 628 { 629 var imgTag = '<IMG WIDTH="700" SRC="images/'; 630 cctv_infowindow = new google.maps.InfoWindow(); 631 cctvLayer = new google.maps.Data(); 632 cctvLayer.loadGeoJson(kCCTVfile); 633 cctvLayer.setStyle(function(feature) 634 { 635 // return the StyleOptions 636 return { 637 icon: makecctvIcon(feature.getProperty("nearVDS")), 638 title: feature.getId() + " " +feature.getProperty('locationName'), 639 visible: false 640 }; 641 }); 642 cctvLayer.addListener('click', function(event) 643 { 644 //console.log("cctv layer was clicked at " + event.feature.getId()); 645 cctvIndex = event.feature.getId(); 646 //console.log(this.title + " is looking for " + this.nearVDS); 647 nearVDS = map.data.getFeatureById(event.feature.getProperty("nearVDS")); 648 currentColor = nearVDS.getProperty("color"); 649 var imgDir = "CCTVFast/"; 650 if (currentColor == "red" || currentColor == "yellow") 651 { 652 imgDir = "CCTVSlow/" 653 } 654 655 cctv_infowindow.setContent('<div style="font-weight:bold;font-family: monospace">' + nearVDS.getId() + " " + currentColor + "<BR>" + imgTag + imgDir + cctvIndex + '.jpg">' + "</div>"); 656 cctv_infowindow.setPosition(event.feature.getGeometry().get()); 657 cctv_infowindow.open(map); 658 659 }); 660 cctvLayer.setMap(map); 661 } 662 663 // Center justify message text in a 16 column field 664 function justifyCMStext(message) 665 { 666 var kBlanks = " "; 667 var padLen = (16 - message.length) / 2; 668 var padding = kBlanks.substring(0, padLen); 669 return padding + message; 670 } 671 672 function handleCMSsubmit() 673 { 674 // recover the user's response 675 var response1 = justifyCMStext(document.getElementById('msgcontent1').value.trim()); 676 var response2 = justifyCMStext(document.getElementById('msgcontent2').value.trim()); 677 var response3 = justifyCMStext(document.getElementById('msgcontent3').value.trim()); 678 var newMsg = response1 + response2 + response3; 679 if (newMsg.length == 0) 680 { 681 alert("Nothing to Send ... Proposed is empty."); 682 } 683 else 684 { 685 document.getElementById('msgdisplay1').value = response1; 686 document.getElementById('msgdisplay2').value = response2; 687 document.getElementById('msgdisplay3').value = response3; 688 saveMessage(response1 + "|" + response2 + "|" + response3); 689 } 690 } 691 692 function handleDialogClose() 693 { 694 // hide the display 695 document.getElementById('dialog').style.display = 'none' 696 } 697 698 function handleCMSclear() 699 { 700 document.getElementById('msgdisplay1').value = ""; 701 document.getElementById('msgdisplay2').value = ""; 702 document.getElementById('msgdisplay3').value = ""; 703 saveMessage("||"); 704 } 705 // retrieve the current cms message file 706 function showMessage(cmsID) 707 { 708 loadAllMessages(); // because someone else may have made a recent update 709 // lookup the message for this cms ID 710 var message = messageDict[cmsID].cms.message; 711 // show the message in the display 712 document.getElementById('msgdisplay1').value = message.phase1.Line1; 713 document.getElementById('msgdisplay2').value = message.phase1.Line2; 714 document.getElementById('msgdisplay3').value = message.phase1.Line3; 715 } 716 function loadAllMessages() 717 { 718 loadJSON("cms_messages.json", function(response) 719 { 720 // Parse JSON string into object 721 messagejson = JSON.parse(response); 722 // Add each message to a lookup dictionary 723 for (var i = 0; i < messagejson.data.length; i++) 724 { 725 var item = messagejson.data[i]; 726 messageDict[item.cms.index] = item; 727 // Set the appropriate icon on the cms icon 728 // set a yellow flag if there's currently no message 729 if (item.cms.message.phase1.Line1 + 730 item.cms.message.phase1.Line2 + 731 item.cms.message.phase1.Line3 == "") 732 { 733 cmsLayer.overrideStyle(cmsLayer.getFeatureById(item.cms.index), {icon: yellowFlag}) 734 } 735 else 736 { 737 cmsLayer.overrideStyle(cmsLayer.getFeatureById(item.cms.index), {icon: blueFlag}) 738 } 739 } 740 }); 741 } 742 743 // Save an updated cms message to the file 744 function saveMessage(outMessage) 745 { 746 // Fetch cmsID from hidden field where it was put when dialog opened. 747 var cmsID = document.getElementById('cmsID').value; 748 //console.log("Saving " + outMessage + " for cmsID " + cmsID) 749 msgParts = outMessage.split("|"); 750 messageDict[cmsID].cms.message.phase1.Line1 = msgParts[0]; 751 messageDict[cmsID].cms.message.phase1.Line2 = msgParts[1]; 752 messageDict[cmsID].cms.message.phase1.Line3 = msgParts[2]; 753 // Set icon to reflect message state 754 if (outMessage == "||") 755 { 756 currentIcon = {icon: yellowFlag}; 757 } 758 else 759 { 760 currentIcon = {icon: blueFlag}; 761 } 762 cmsLayer.overrideStyle(cmsLayer.getFeatureById(cmsID), currentIcon) 763 outString = "{\n\t\"data\":\n\t\t" + JSON.stringify(Object.values(messageDict)) + "}"; 764 765 var xhttp = new XMLHttpRequest(); 766 xhttp.open("GET", "cgi-bin/saveMessage.py?msg=" + outString, true); 767 xhttp.send(); 768 // Using POST might be a better idea ... haven't tried this yet 769 // var xhr = new XMLHttpRequest(); 770 // xhr.open("POST", "/cgi-bin/saveMessage.py?", true); 771 // xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); 772 // send the collected data as JSON 773 // xhr.send(JSON.stringify(messageList)); 233 initCCTVbutton(); 234 initCMSbutton(); 235 initVDSbutton(); 774 236 } 775 237 … … 786 248 streetViewControl: false 787 249 }); 788 loadVDSlayer(); // go load the map data 250 789 251 // setup the search box and center button 790 252 initSearch(); 791 253 initCenter(); 792 254 793 loadCMSlayer(); 794 795 loadCCTVlayer();796 797 initLayerButtons(); 255 loadVDSlayer(); // go load the map data 256 loadCMSlayer(); 257 loadCCTVlayer(); 258 259 initLayerButtons(); // setup the show/hide layer buttons 798 260 799 261 // Start a timer to refresh the vds dots every 10 seconds … … 801 263 // start an interval timer to refresh the cms icons every 10 seconds 802 264 var cmsTimer = setInterval(loadAllMessages, 10000); 803 // Listen for zoom changes and move the placePins so as to keep a nice265 // Listen for zoom changes and move the vds dots so as to keep a nice 804 266 // visual distance between them appropriate to the zoom factor 805 267 map.addListener('zoom_changed', function() … … 816 278 817 279 } 818 // Styles array for Night Mode map 819 // Ref: https://developers.google.com/maps/documentation/javascript/styling 820 var night_mode = [ 821 {elementType: 'geometry', stylers: [{color: '#242f3e'}]}, 822 {elementType: 'labels.text.stroke', stylers: [{color: '#242f3e'}]}, 823 {elementType: 'labels.text.fill', stylers: [{color: '#746855'}]}, 824 { 825 featureType: 'administrative.locality', 826 elementType: 'labels.text.fill', 827 stylers: [{color: '#d59563'}] 828 }, 829 { 830 featureType: 'poi', 831 elementType: 'labels.text.fill', 832 stylers: [{color: '#d59563'}] 833 }, 834 { 835 featureType: 'poi.park', 836 elementType: 'geometry', 837 stylers: [{color: '#263c3f'}] 838 }, 839 { 840 featureType: 'poi.park', 841 elementType: 'labels.text.fill', 842 stylers: [{color: '#6b9a76'}] 843 }, 844 { 845 featureType: 'road', 846 elementType: 'geometry', 847 stylers: [{color: '#38414e'}] 848 }, 849 { 850 featureType: 'road', 851 elementType: 'geometry.stroke', 852 stylers: [{color: '#212a37'}] 853 }, 854 { 855 featureType: 'road', 856 elementType: 'labels.text.fill', 857 stylers: [{color: '#9ca5b3'}] 858 }, 859 { 860 featureType: 'road.highway', 861 elementType: 'geometry', 862 stylers: [{color: '#746855'}] 863 }, 864 { 865 featureType: 'road.highway', 866 elementType: 'geometry.stroke', 867 stylers: [{color: '#1f2835'}] 868 }, 869 { 870 featureType: 'road.highway', 871 elementType: 'labels.text.fill', 872 stylers: [{color: '#f3d19c'}] 873 }, 874 { 875 featureType: 'transit', 876 elementType: 'geometry', 877 stylers: [{color: '#2f3948'}] 878 }, 879 { 880 featureType: 'transit.station', 881 elementType: 'labels.text.fill', 882 stylers: [{color: '#d59563'}] 883 }, 884 { 885 featureType: 'water', 886 elementType: 'geometry', 887 stylers: [{color: '#17263c'}] 888 }, 889 { 890 featureType: 'water', 891 elementType: 'labels.text.fill', 892 stylers: [{color: '#515c6d'}] 893 }, 894 { 895 featureType: 'water', 896 elementType: 'labels.text.stroke', 897 stylers: [{color: '#17263c'}] 898 } 899 ] 900 901 // Using John's API Key 280 902 281 </script> 282 <script src="js/common.js"></script> 283 <script src="js/vdsLayer.js"></script> 284 <script src="js/cmsLayer.js"></script> 285 <script src="js/cctvLayer.js"></script> 286 <script src="js/controls.js"></script> 287 <script src="js/night_mode.js"></script> 288 <!-- Using John's API Key --> 903 289 <script async defer 904 290 src="https://maps.googleapis.com/maps/api/js?key=AIzaSyD6iTyN0DjP-9OVkAgicyp4tkC10naE_B8&libraries=places&callback=initMap">
Note: See TracChangeset
for help on using the changeset viewer.
