Warning: Can't use blame annotator:
svn blame failed on trunk/src/cptms/cptms_map.html: ("Can't find a temporary directory: Internal error", 20014)

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

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

cptms_map.html Modified to fix layout/format problems in cms edit dialog.

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