source: tmcsimulator/trunk/src/cptms/cptms_map.html @ 304

Revision 304, 29.3 KB checked in by jdalbey, 7 years ago (diff)

cptms_map.html Added cctv icons

Line 
1<!DOCTYPE html>
2<html>
3  <head>
4<!-- Launch with  python -m CGIHTTPServer 8080  -->
5  <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
6    <title>CPTMS Map v0.5.6</title> 
7    <style>
8        @font-face {
9          font-family: Scoreboard;
10          src: url('scoreboard.ttf');
11        }
12      /* Set the size of the div element that contains the map */
13      #mapdiv {
14        height: 100%;
15        width: 100%; 
16       }
17        /* Makes the page fill the window. */
18      html, body {
19        height: 100%;
20        margin: 0;
21        padding: 0;
22      }
23        textarea {
24           height: 33px;
25           width: 272px;
26           resize: none;
27           font-family: Scoreboard;
28           font-size: xx-large;
29           background-color: #2F4F4F;
30           color: yellow;
31        }
32       input {
33           border: thin solid #333;
34           padding: 2px;
35           font-family: monospace;
36           font-size: large;
37        }
38      #search-input {
39        background-color: #17263c;  /* #CD853F;  /*#E6E6FA; /* lavender */
40        color: #E6E6FA;  /* #FFEFD5; */
41        font-family: Roboto;
42        font-size: 18px;
43        font-weight: 400;
44        margin-left: 12px;
45        padding: 0 11px 0 13px;
46        text-overflow: ellipsis;
47        border-color: #746855;   /* #4d90fe; */
48        width: 400px;
49      }
50
51      #search-input:focus {
52        border-color: #E6E6FA; 
53      }
54
55      #ctrButton {
56        font-size: 40px;
57        margin-right: 9px;
58        background-color: #fff;
59        color: #47476b;
60        cursor: pointer;
61      }
62      #cms-info-label {
63           height: 20px;
64           width: 540px;
65           overflow: hidden;
66           background-color: #A8C5FF;  /*#ECECFB; */
67           border: thin solid #BDBDBD;
68           padding: 5px;
69       }
70      #message-display {
71           height: 172px;
72           width: 300px;
73           overflow: hidden;
74           float: left;
75       }
76      #message-input {
77           height: 122px;
78           width: 135px;
79           background-color: #729FFF;
80           float: left;
81       }
82       #buttonPanel {
83           height: 122px;
84           width: 100px;
85           background-color: #729FFF;
86           border-left: none;
87           float: left;
88           padding: 20px;
89        }
90       .wrapper {
91           position: relative;\
92        }
93       #dialog {
94          position: absolute;
95          top: 10%;
96          right: 20%;
97          background-color: #729FFF; /* #ECECFB; */
98          margin: auto;
99          padding: 20px;
100          border: 1px solid #888;
101          width: 52%;
102          display: none;           
103        }
104      .unstyled-button {
105        border: 0 none;
106        padding: 0;
107        background: none;
108        cursor: pointer;
109      }   
110    /* The Close Button */
111    .close {
112      color: orange;
113      float: right;
114      font-size: 20px;
115      font-weight: bold;
116    }
117    .close:hover,
118    .close:focus {
119      color: red;
120      text-decoration: none;
121      cursor: pointer;
122    }
123
124    </style>
125  </head>
126  <body>
127    <!--
128         Version 5.7 integrates CCTV icons and button (but empty click handler)
129         Version 5.6 integrates CMS features
130         Version 5.5 renames title to CPTMS, loads static data on startup and dynamic data
131         every ten seconds. 
132         Version 5.4 adds Search box and Center button
133         Version 5.3 fixed dot color update defect, increased refresh rate to 10 sec.
134         Version 5.2 places red dots overlapping yellow dots.
135         Version 5.1 removes the map and street view buttons and the H3 tag.
136         Version 5 uses precomputed perpendicular vector in dot adjustment function
137         Version 4 Adjust the spacing between dots when the map is zoomed.
138         Version 3 does loadGeoJson only once, and subsequently does an ajax load
139         of the highways file, and selectively updates only those placePins whose
140         color has changed.
141         @author jdalbey  2019.2.17
142    -->
143    <!-- The text area input for the Search Box -->
144    <input id="search-input" class="controls" type="text" placeholder="Search Box">
145    <!--The div element where the map appears -->
146    <div id="mapdiv"></div>
147    <!--The div element where the buttons appears -->
148    <div id="ctrButton" class="unstyled-button">&#x2295;</div>
149    <button id="cctvButton" class="unstyled-button"><img id="cctvBtnImg" src="images/CPTMSImages/btnReady_CCTV.png"></button>
150    <button id="cmsButton" class="unstyled-button"><img id="cmsBtnImg" src="images/CPTMSImages/btnReady_CMS.png"></button>
151    <button id="vdsButton" class="unstyled-button"><img id="vdsBtnImg" src="images/CPTMSImages/btnDepressed_VDS.png"></button>
152    <!-- The div element for the popup dialog -->
153    <div id="dialog" style="display:none;">
154        <span class="close">&times;</span>
155        <br>
156        <div id="cms-info-label" style="font-family:monospace">CMS ID: xxx LOCATION: </div>
157        <br>
158        <div><pre>Proposed:                              Current:</pre></div>
159        <input id='cmsID' value="" type='hidden'/>
160        <div id="message-input">
161        <input id="msgcontent1" size="16" maxlength="16" type="text"/><br><br>
162        <input id="msgcontent2" size="16" maxlength="16"/><br><br>
163        <input id="msgcontent3" size="16" maxlength="16"/>
164        </div>       
165        <div id="buttonPanel"    style="display: block;">
166        <button onclick="handleSubmit();">Send >></button><br>
167        <button onclick="handleClear();">Clear >></button><br>
168        <button onclick="handleClose();">Close </button>
169        </div>
170        <div id="message-display"  style="display: block;">
171         <textarea readonly id="msgdisplay1" maxlength="16" rows="1" cols="16"></textarea>
172         <textarea readonly id="msgdisplay2" maxlength="16" rows="1" cols="16"></textarea>
173         <textarea readonly id="msgdisplay3" maxlength="16" rows="1" cols="16"></textarea>
174        </div>
175    </div>
176
177    <script>
178    // a global variable for the google map
179    var map; 
180    // a global dictionary to lookup a station's original coordinates
181    var vds_coords = {};
182    // a global variable to hold locations of marked search places
183    var placePins = [];
184    // Constant name of dynamic json data file created by CADserver
185    var kMapPointsFile = "highways.json";
186    // Constant name of initial (static) highways file used once at startup
187    var kMapStartupFile = "highways_startup.json";
188    // Constant for map center location: The John Wayne Airport
189    //var centerPoint = {lat: 33.687228, lng: -117.872148};
190    // Constant for map center location in District 12
191    var centerPoint = {lat: 33.693385, lng: -117.798937};
192    // Initial map zoom
193    var initZoom = 11;
194    // Dot colors used in traffic model to indicate free-flowing, slowed, and stopped traffic
195    // and their associated zvalues so slower traffic dots are more visible.
196    // white means a disabled spot
197    var colorZvalues = {"white":5,"lime":10,"yellow":20,"red":30};
198var kCMSstartupFile = "cmsStatusD12.json";
199var kCCTVfile = "cctv_locations_D12.json";
200var blueFlag = "images/CPTMSImages/icon_cmsBlue.png";
201var yellowFlag = "images/CPTMSImages/icon_cmsYellow.png";
202var cctvIcon = "images/CPTMSImages/icon_cctvBlue.png";
203var messageList;
204var cms_info;
205var cmsList = [];
206var cctvList = [];
207var cms_showing = false;
208var vds_showing = true;
209var cctv_showing = false;
210    // Build a solid colored icon to use instead of the classic pin
211    // Use a diamond on N and E directions, circle on S and W directions
212    function dotSymbol(color,postmileID) //,direction)
213    {
214        var circle = google.maps.SymbolPath.CIRCLE;
215        var diamond = 'M -1,0 0,-1 1,0 0,1 z';
216        var myShape = circle;
217        // See if postmile name contains N or W letters
218        //if ((postmileID.indexOf('N') != -1) || (postmileID.indexOf('W') != -1))
219        //{
220        //   myShape = diamond
221        //}
222        return {
223            path: myShape,
224            scale: 5,
225            strokeColor: "black", // the border color
226            strokeWeight: 1,      // the border thickness
227            fillColor: color,
228            fillOpacity: 1.0
229        };
230    }
231
232    // Load the map data from a json file and style all the points
233    function loadMapData()
234    {
235        // Load the static map data and call saveCoords when done
236        map.data.loadGeoJson(kMapStartupFile,null,saveCoords)
237
238        // Style the map data by applying the desired properties to each feature (marker)
239        // The function will be called every time a feature's properties are updated.
240        map.data.setStyle(function(feature) 
241        {
242            // Get the postmile id
243            var name = feature.getId();
244            // Get the desired color value
245            var ptColor = feature.getProperty("color");
246            var street = feature.getProperty("street");
247            // Build the marker
248            var iconSymbol = dotSymbol(ptColor,name);
249            // return the StyleOptions
250            return {
251                    icon: iconSymbol,
252                    title: name + " @" + street,  // set rollover text
253                    // set zIndex for slowed traffic to a higher value so they overlap
254                    zIndex: colorZvalues[ptColor]
255                   };
256        });
257    }
258    // callback when load GeoJson completes
259    // save each feature's Point as the original coordinates for later reference
260    function saveCoords(features)
261    {
262        // Iterate over all the features in the map
263        features.forEach(function(feature)
264        {
265            var pt = feature.getGeometry().get(); 
266            vds_coords[feature.getId()] = pt;  // save the Point in a dictionary
267        });
268        // update the dot colors from the dynamic json data
269        updateMap();
270        // go adjust the marker coordinates so dots don't overlap
271        adjustCoords(calcDistanceFactor());
272    }
273
274    // magic formula controls distance between dots proportionate to zoom factor
275    function calcDistanceFactor()
276    {
277        // 15 is maximum zoom, the point at which no adjustment is needed
278        return (.0005*(15-map.getZoom())); 
279    }
280
281    // Adjust the coordinates of dots so they appear side-by-side
282    // The perpendicular vector for each dot has been provided,
283    // so we just need to multiply by a scaling factor (adjAmount)
284    // @param adjAmount amount by which to adjust coordinate
285    function adjustCoords(adjAmount)
286    {
287        // Adjust the NB points a slight amount
288        map.data.forEach(function(feature)
289        {
290            // get the name of the current feature
291            var name = feature.getId();
292            // lookup the original coordinates for this feature
293            var coords = vds_coords[name];
294
295            //retrieve the perpendicular vector (precomputed)
296            var perpx = feature.getProperty("perpx")
297            var perpy = feature.getProperty("perpy")
298            // Make adjustment and save it
299            var myLat = coords.lat() + perpy * adjAmount
300            var myLong = coords.lng() + perpx * adjAmount
301            feature.setGeometry({lat:myLat, lng:myLong});
302        });
303    }
304
305     // update the color (as needed) for a given marker
306     function updateMarker(marker)
307     {
308        target = marker.id;
309        newColor = marker.properties.color;
310        // see if new color is different than current color
311        currentFeature = map.data.getFeatureById(target);
312        currentColor = currentFeature.getProperty("color");
313        // if a new color is desired then assign it to the feature's color property
314        if (currentColor != newColor)
315        {
316            currentFeature.setProperty("color",newColor);
317            // set zIndex for slowed traffic to a higher value so they overlap
318            currentFeature.setProperty("zIndex", colorZvalues[newColor]);
319        }
320     }
321
322    // Load the dynamic json file for highways, etc.
323    // Ref: https://codepen.io/KryptoniteDove/post/load-json-file-locally-using-pure-javascript
324function loadJSON(inFile, callback)
325{
326    var xobj = new XMLHttpRequest();
327    xobj.overrideMimeType("application/json");
328    xobj.open('GET', inFile, true);
329    xobj.onreadystatechange = function()
330    {
331        if (xobj.readyState == 4 && xobj.status == "200")
332        {
333            callback(xobj.responseText);
334        }
335    };
336    // We want ajax to ignore any cached responses
337    xobj.setRequestHeader('If-Modified-Since', 'Sat, 01 Jan 2000 01:01:01 GMT')
338    xobj.send(null);
339}
340     // Load the highways dynamic json file and update the map
341     function updateMap()
342     {
343        var parsed_JSON;
344        loadJSON(kMapPointsFile,function(response)
345        {
346            // Parse JSON string into object
347            parsed_JSON = JSON.parse(response);
348            // Process each new marker - lookup in current map
349            parsed_JSON.features.forEach(updateMarker);
350        });
351     }
352
353    // Initialize the center button (to re-center the map)
354    function initCenter()
355    {
356        var centerBtnDiv = document.getElementById('ctrButton');
357        map.controls[google.maps.ControlPosition.RIGHT_CENTER].push(centerBtnDiv)
358        centerBtnDiv.title = 'Click to recenter the map';
359
360        // Setup the click event listeners: reset center location and zoom factor
361        centerBtnDiv.addEventListener('click', function() {
362          map.setCenter(centerPoint);
363          map.setZoom(initZoom);
364          clearPlacePins();
365        });
366    }
367
368    // Initialize the search box and listener
369    function initSearch()
370    {
371        // Create the search box and link it to the UI element.
372        var input = document.getElementById('search-input');
373        var searchBox = new google.maps.places.SearchBox(input);
374        map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);
375
376        // Bias the SearchBox results towards current map's viewport.
377        map.addListener('bounds_changed', function() {
378          searchBox.setBounds(map.getBounds());
379        });
380
381        // Listen for the event fired when the user selects a prediction and retrieve
382        // more details for that place.
383        searchBox.addListener('places_changed', function() {
384          var places = searchBox.getPlaces();
385
386          if (places.length == 0) {
387            return;
388          }
389
390          clearPlacePins();
391
392          // Create a bounding region to include the search result places
393          var bounds = new google.maps.LatLngBounds();
394          // For each place, get the icon, name and location.
395          // There may be multiple search results
396          places.forEach(function(place) {
397            if (!place.geometry) {
398              console.log("Returned place contains no geometry");
399              return;
400            }
401
402            // Create a marker for each place.
403            placeMarker = new google.maps.Marker({
404              map: map,
405              title: place.name,
406              position: place.geometry.location
407            })
408
409            // Click on the marker to remove it from the display
410            placeMarker.addListener('click', function() {
411              placeMarker.setMap(null);
412            });
413
414            // Add this marker to the collection of current markers
415            placePins.push(placeMarker);
416
417            // Create a bounding region to include this place
418            if (place.geometry.viewport) {
419              // Only geocodes have viewport.
420              bounds.union(place.geometry.viewport);
421            } else {
422              bounds.extend(place.geometry.location);
423            }
424          });
425          // This will pan and zoom to the area around the marker
426          //map.fitBounds(bounds);
427          // This will center the map on the new marker(s) but not zoom
428          map.setCenter(bounds.getCenter());
429        });
430    }
431
432    // Remove any place pins from a previous search
433    function clearPlacePins()
434    {
435          placePins.forEach(function(marker) {
436            marker.setMap(null);
437          });
438          placePins = [];
439    }
440// Initialize the view/hide buttons
441function initButton()
442{
443    var cctvBtnDiv = document.getElementById('cctvButton');
444    map.controls[google.maps.ControlPosition.LEFT_BOTTOM].push(cctvBtnDiv)
445    cctvBtnDiv.title = 'Click to toggle cctv view';
446    // Setup the click event listeners to toggle icon display
447    cctvBtnDiv.addEventListener('click', function() {
448            cctv_showing = !cctv_showing;
449            // reveal or hide all the icons
450            for (var i = 0; i < cctvList.length; i++)
451            {
452                cctvList[i].setVisible(cctv_showing);
453            }
454            // Determine which button image to show
455            if (cctv_showing)
456            {
457                pic = "images/CPTMSImages/btnDepressed_CCTV.png"
458            }
459            else
460            {
461                pic = "images/CPTMSImages/btnReady_CCTV.png"
462            }
463            document.getElementById('cctvBtnImg').src=pic;
464    });
465
466    var cmsBtnDiv = document.getElementById('cmsButton');
467    map.controls[google.maps.ControlPosition.LEFT_BOTTOM].push(cmsBtnDiv)
468    cmsBtnDiv.title = 'Click to toggle cms view';
469    // Setup the click event listeners to toggle icon display
470    cmsBtnDiv.addEventListener('click', function() {
471            cms_showing = !cms_showing;
472            // reveal or hide all the icons
473            for (var i = 0; i < cmsList.length; i++)
474            {
475                cmsList[i].setVisible(cms_showing);
476            }
477            // Determine which button image to show
478            if (cms_showing)
479            {
480                pic = "images/CPTMSImages/btnDepressed_CMS.png"
481            }
482            else
483            {
484                pic = "images/CPTMSImages/btnReady_CMS.png"
485            }
486            document.getElementById('cmsBtnImg').src=pic;
487    });
488
489    var vdsBtnDiv = document.getElementById('vdsButton');
490    map.controls[google.maps.ControlPosition.LEFT_BOTTOM].push(vdsBtnDiv)
491    vdsBtnDiv.title = 'Click to toggle vds view';
492
493    // Setup the click event listeners to toggle icon display
494    vdsBtnDiv.addEventListener('click', function() {
495            vds_showing = !vds_showing;
496            // reveal or hide all the dots
497            map.data.forEach(function(feature)
498            {
499                map.data.overrideStyle(feature, {visible:vds_showing});
500            });
501            // Determine which button image to show
502            if (vds_showing)
503            {
504                pic = "images/CPTMSImages/btnDepressed_VDS.png"
505            }
506            else
507            {
508                pic = "images/CPTMSImages/btnReady_VDS.png"
509            }
510            document.getElementById('vdsBtnImg').src=pic;
511    });
512}
513function setCMSmarkers()
514{
515    var simpleImage = "";
516    loadJSON(kCMSstartupFile, function(response)
517    {
518        // Parse JSON string into object
519        cms_info = JSON.parse(response);
520        //console.log(cms_info.data[0].cms);
521        // Process each new marker
522        for (var i = 0; i < cms_info.data.length; i++)
523        {
524            var cms = cms_info.data[i].cms;
525            var currLat = Number(cms.location.latitude);
526            var currLong = Number(cms.location.longitude);
527            // load a yellow flag if there's currently no message
528            if (messageList[i] == "||")
529                simpleImage = yellowFlag;
530            else
531                simpleImage = blueFlag;
532            var directionCode = cms.location.direction.charAt(0);
533            var locationInfo = directionCode + " " + cms.location
534                .route + " " + cms.location.postmile + " " + cms
535                .location.locationName
536            cmsList[i] = new google.maps.Marker(
537            {
538                position:
539                {
540                    lat: currLat,
541                    lng: currLong
542                },
543                map: map,
544                icon: simpleImage,
545                title: "#"+i+" " +locationInfo,
546                cmsid: "" + i,
547                location: locationInfo
548            });
549            cmsList[i].setVisible(false); // initially hidden
550            google.maps.event.addListener(cmsList[i], 'click',
551                function()
552                {
553                    var dialog = document.getElementById('dialog');
554                    dialog.style.display = 'block';
555                    // fetch the sequential msg #
556                    cmsID = Number(this.cmsid);
557                    // Assign to the hidden field
558                    document.getElementById('cmsID').value = cmsID;
559                    getMessage(cmsID); // note: this is async
560                    document.getElementById('cms-info-label').innerHTML = "CMS ID: " +
561                        cmsID + "&nbsp;&nbsp;&nbsp;LOCATION: " + this.location;
562                    // clear input fields
563                    document.getElementById('msgcontent1').value = "";
564                    document.getElementById('msgcontent2').value = "";
565                    document.getElementById('msgcontent3').value = "";
566                    document.getElementById('msgcontent1').focus();
567                    var span = document.getElementsByClassName("close")[0]
568                    // When the user clicks on <span> (x), close the modal
569                    span.onclick = function() {
570                      handleClose();
571                    }
572                });
573        }
574    });
575}
576function setCCTVmarkers()
577{
578    loadJSON(kCCTVfile, function(response)
579    {
580        // Parse JSON string into object
581        cctv_info = JSON.parse(response);
582        // Process each new marker
583        for (var i = 0; i < cctv_info.data.length; i++)
584        {
585            var cctv = cctv_info.data[i].cctv;
586            var currLat = Number(cctv.location.latitude);
587            var currLong = Number(cctv.location.longitude);
588            var locationInfo = cctv.location.locationName;
589            cctvList[i] = new google.maps.Marker(
590            {
591                position:
592                {
593                    lat: currLat,
594                    lng: currLong
595                },
596                map: map,
597                icon: cctvIcon,
598                title: "#"+i+" " +locationInfo,
599                cctvid: "" + i,
600                location: locationInfo,
601                index: cctv.index,
602                nearVDS: cctv.location.nearVDS
603            });
604            cctvList[i].setVisible(false); // initially hidden
605            google.maps.event.addListener(cctvList[i], 'click',
606                function()
607                {
608                    // fetch the sequential msg #
609                    cctvIndex = this.index;
610                    alert("here will be the view of cctv: " + cctvIndex);
611                });
612        }
613    });
614}
615
616
617
618
619function handleSubmit()
620{
621    // recover the user's response
622    var response1 = document.getElementById('msgcontent1').value;
623    var response2 = document.getElementById('msgcontent2').value;
624    var response3 = document.getElementById('msgcontent3').value;
625    var newMsg = response1+response2+response3;
626    if (newMsg.length == 0)
627    {
628        alert("Nothing to Send ... Proposed is empty.");
629    }
630    else
631    {
632        document.getElementById('msgdisplay1').value = response1;
633        document.getElementById('msgdisplay2').value = response2;
634        document.getElementById('msgdisplay3').value = response3;
635        saveMessage(response1 + "|" + response2 + "|" + response3);
636    }
637}
638
639function handleClose()
640{
641    // hide the display
642    document.getElementById('dialog').style.display = 'none'
643}
644
645function handleClear()
646{
647    document.getElementById('msgdisplay1').value = "";
648    document.getElementById('msgdisplay2').value = "";
649    document.getElementById('msgdisplay3').value = "";
650    saveMessage("||");
651}
652// retrieve the current cms message file
653function getMessage(cmsID)
654{
655    loadJSON("http://localhost:8080/messagefile.txt", function(response)
656    {
657        // Parse JSON string into object
658        messageList = JSON.parse(response);
659        // select a message from json for the given cmsID
660        console.log("get by cmsID=" + cmsID);
661        var cmsSign = document.getElementById('msgdisplay1');
662        messageparts = messageList[cmsID].split("|");
663        cmsSign.value = messageparts[0];
664        document.getElementById('msgdisplay2').value = messageparts[1];
665        document.getElementById('msgdisplay3').value = messageparts[2];
666    });
667}
668// Save an updated cms message to the file
669// NB: cms id's are one-based, json array is zero-based.
670function saveMessage(outMessage, cmsID)
671{
672    var cmsID = document.getElementById('cmsID').value;
673    console.log("Saving " + outMessage + " for cmsID " + cmsID)
674    messageList[cmsID] = outMessage;
675    // Change icon if something was saved
676    if (outMessage == "||")
677        cmsList[cmsID].setIcon(yellowFlag);
678    else
679        cmsList[cmsID].setIcon(blueFlag);
680
681    var xhttp = new XMLHttpRequest();
682    xhttp.open("GET", "http://localhost:8080/cgi-bin/saveMessage.py?msg=" + JSON
683        .stringify(messageList), true);
684    xhttp.send();
685    // Using POST might be a better idea ... haven't tried this yet
686    //      var xhr = new XMLHttpRequest();
687    //      xhr.open("POST", "/cgi-bin/saveMessage.py?", true);
688    //      xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8');
689    // send the collected data as JSON
690    //      xhr.send(JSON.stringify(messageList));
691} 
692
693    // Initialize the map and load the points
694    function initMap() 
695    {
696        // Declare the map and where it belongs on the page
697        map = new google.maps.Map( document.getElementById('mapdiv'), 
698        {
699            zoom: initZoom, 
700            center: centerPoint,
701            styles: night_mode,
702            mapTypeControl: false,
703            streetViewControl: false 
704        });
705        loadMapData();  // go load the map data
706        // setup the search box and center button
707        initSearch();
708        initCenter();
709        initButton();
710        getMessage(1); // load the current message file
711        setCMSmarkers();
712        setCCTVmarkers();
713
714        // Start a timer to refresh the map every 10 seconds
715        var myTimer = setInterval(updateMap, 10000);
716        // Listen for zoom changes and move the placePins so as to keep a nice
717        // visual distance between them appropriate to the zoom factor
718        map.addListener('zoom_changed', function() {
719            // fetch how much the map is currently zoomed
720            currentZoom = map.getZoom(); 
721            // only bother adjusting within this range
722            if ((currentZoom <16) && (currentZoom>10))
723            {
724                // magic formula controls distance between dots
725                adjustCoords(calcDistanceFactor());
726            }
727        });
728
729    }
730
731    // Styles array for Night Mode map
732    // Ref: https://developers.google.com/maps/documentation/javascript/styling
733    var night_mode = [
734                {elementType: 'geometry', stylers: [{color: '#242f3e'}]},
735                {elementType: 'labels.text.stroke', stylers: [{color: '#242f3e'}]},
736                {elementType: 'labels.text.fill', stylers: [{color: '#746855'}]},
737                {
738                  featureType: 'administrative.locality',
739                  elementType: 'labels.text.fill',
740                  stylers: [{color: '#d59563'}]
741                },
742                {
743                  featureType: 'poi',
744                  elementType: 'labels.text.fill',
745                  stylers: [{color: '#d59563'}]
746                },
747                {
748                  featureType: 'poi.park',
749                  elementType: 'geometry',
750                  stylers: [{color: '#263c3f'}]
751                },
752                {
753                  featureType: 'poi.park',
754                  elementType: 'labels.text.fill',
755                  stylers: [{color: '#6b9a76'}]
756                },
757                {
758                  featureType: 'road',
759                  elementType: 'geometry',
760                  stylers: [{color: '#38414e'}]
761                },
762                {
763                  featureType: 'road',
764                  elementType: 'geometry.stroke',
765                  stylers: [{color: '#212a37'}]
766                },
767                {
768                  featureType: 'road',
769                  elementType: 'labels.text.fill',
770                  stylers: [{color: '#9ca5b3'}]
771                },
772                {
773                  featureType: 'road.highway',
774                  elementType: 'geometry',
775                  stylers: [{color: '#746855'}]
776                },
777                {
778                  featureType: 'road.highway',
779                  elementType: 'geometry.stroke',
780                  stylers: [{color: '#1f2835'}]
781                },
782                {
783                  featureType: 'road.highway',
784                  elementType: 'labels.text.fill',
785                  stylers: [{color: '#f3d19c'}]
786                },
787                {
788                  featureType: 'transit',
789                  elementType: 'geometry',
790                  stylers: [{color: '#2f3948'}]
791                },
792                {
793                  featureType: 'transit.station',
794                  elementType: 'labels.text.fill',
795                  stylers: [{color: '#d59563'}]
796                },
797                {
798                  featureType: 'water',
799                  elementType: 'geometry',
800                  stylers: [{color: '#17263c'}]
801                },
802                {
803                  featureType: 'water',
804                  elementType: 'labels.text.fill',
805                  stylers: [{color: '#515c6d'}]
806                },
807                {
808                  featureType: 'water',
809                  elementType: 'labels.text.stroke',
810                  stylers: [{color: '#17263c'}]
811                }
812              ]
813
814    // Using John's API Key
815    </script>
816    <script async defer
817    src="https://maps.googleapis.com/maps/api/js?key=AIzaSyD6iTyN0DjP-9OVkAgicyp4tkC10naE_B8&libraries=places&callback=initMap">
818    </script>
819  </body>
820</html>
Note: See TracBrowser for help on using the repository browser.