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

Revision 305, 31.1 KB checked in by jdalbey, 7 years ago (diff)

cptms_map.html v 0.6 adds dynamic cctv images

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