Index: /trunk/src/cptms/cptms.html
===================================================================
--- /trunk/src/cptms/cptms.html	(revision 311)
+++ /trunk/src/cptms/cptms.html	(revision 312)
@@ -3,6 +3,7 @@
   <head>
 <!-- Launch with  python -m CGIHTTPServer 80  -->
+<!-- map center button icon from http://icons8.com/.  (Obligatory backlink, don't remove ) -->
   <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
-    <title>CPTMS Map v0.6.1</title> 
+    <title>CPTMS Map v0.6.2</title> 
     <style>
         @font-face {
@@ -36,8 +37,4 @@
            font-size: medium;
         }
-        /*img.resize {
-          max-width:auto;
-          max-height:500px;
-        }*/
       #search-input {
         background-color: #17263c;  /* #CD853F;  /*#E6E6FA; /* lavender */
@@ -56,11 +53,9 @@
         border-color: #E6E6FA; 
       }
-
       #ctrButton {
-        font-size: 40px;
-        margin-right: 9px;
+        cursor: pointer;
         background-color: #fff;
-        color: #47476b;
-        cursor: pointer;
+        margin-right: 7px;
+        border: thick solid white;
       }
       #cms-info-label {
@@ -95,8 +90,4 @@
            position: relative;\
         }
-.gm-style-iw {
-background-color: #729FFF;
-border-color: #729FFF;
-}
        #dialog {
           position: absolute;
@@ -116,5 +107,5 @@
         cursor: pointer;
       }    
-    /* The Close Button */
+    /* The Close Button in the CMS Dialog */
     .close {
       color: #2E2E2E;
@@ -134,4 +125,5 @@
   <body>
     <!-- 
+         Version 6.2 puts cctv and cms and vds in separate data layers.   
          Version 6.1 Puts cms messages in json formatted file.  Polls for updates.
          Version 6.0 Adds speed-dependent images to infowindow for cctv icons
@@ -156,9 +148,4 @@
     <!--The div element where the map appears -->
     <div id="mapdiv"></div>
-    <!--The div element where the buttons appears -->
-    <div id="ctrButton" class="unstyled-button">&#x2295;</div>
-    <button id="cctvButton" class="unstyled-button"><img id="cctvBtnImg" src="images/CPTMSImages/btnReady_CCTV.png"></button>
-    <button id="cmsButton" class="unstyled-button"><img id="cmsBtnImg" src="images/CPTMSImages/btnReady_CMS.png"></button>
-    <button id="vdsButton" class="unstyled-button"><img id="vdsBtnImg" src="images/CPTMSImages/btnDepressed_VDS.png"></button>
     <!-- The div element for the popup dialog -->
     <div id="dialog" style="display:none;">
@@ -174,7 +161,7 @@
         </div>        
         <div id="buttonPanel"    style="display: block;">
-        <button onclick="handleSubmit();">Send >></button><br>
-        <button onclick="handleClear();">Clear >></button><br>
-        <button onclick="handleClose();">Close </button>
+        <button onclick="handleCMSsubmit();">Send >></button><br>
+        <button onclick="handleCMSclear();">Clear >></button><br>
+        <button onclick="handleDialogClose();">Close </button>
         </div>
         <div id="message-display"  style="display: block;">Current:
@@ -184,13 +171,14 @@
         </div>
     </div>
+    <!--The div elements where the buttons appear  -->
+    <div id="ctrButton"><img width="30" src="images/CPTMSImages/btn_mapcenter.png"</div>
+    <button id="cctvButton" class="unstyled-button"><img id="cctvBtnImg" src="images/CPTMSImages/btnReady_CCTV.png"></button>
+    <button id="cmsButton" class="unstyled-button"><img id="cmsBtnImg" src="images/CPTMSImages/btnReady_CMS.png"></button>
+    <button id="vdsButton" class="unstyled-button"><img id="vdsBtnImg" src="images/CPTMSImages/btnDepressed_VDS.png"></button>
 
     <script>
-//TODO:  Can we speed up vds loading?  can we load the data in the background
-// only display dots when the button is clicked?  Does using setStyle slow it down? Instead of setstyle should we simply set the color on each marker (like we do for cms icons?)  What's the difference between a feature created by loadGeoJson and just a marker added individually?
-// would pre-computing coords for each zoom level and have them accessed in a table make a difference?
-// Add interval refresh for cms icon colors.  
+//TODO:  
 // Add phase 2 to messages.
 // cms set visible gets undefined error after last icon is processed.
-// have we solved the duplicate cms defect?
 
     // a global variable for the google map
@@ -228,40 +216,53 @@
     var cctvIcon = "images/CPTMSImages/icon_cctvCyan.png";
     var cctvIconWhite = "images/CPTMSImages/icon_cctvWhite.png";
-
+    var vdsIconGreen = "images/CPTMSImages/circle-green.png"
+    var vdsIconYellow = "images/CPTMSImages/circle-yellow.png"
+    var vdsIconRed = "images/CPTMSImages/circle-red.png"
     var cms_info;
-    var cmsList = [];
     var messageDict = {};
-    var cctvList = [];
     var cms_showing = false;
     var vds_showing = true;
     var cctv_showing = false;
+    var cctv_infowindow; // We create just a single instance of info window.
+
     // Build a solid colored icon to use instead of the classic pin
     // Use a diamond on N and E directions, circle on S and W directions
-    function dotSymbol(color, postmileID) //,direction)
-    {
-        var circle = google.maps.SymbolPath.CIRCLE;
-        var diamond = 'M -1,0 0,-1 1,0 0,1 z';
-        var myShape = circle;
-        // See if postmile name contains N or W letters
-        //if ((postmileID.indexOf('N') != -1) || (postmileID.indexOf('W') != -1))
-        //{
-        //   myShape = diamond
-        //}
+    function dotSymbol(color) //,direction)
+    {
+//        var circle = google.maps.SymbolPath.CIRCLE;
+//        var diamond = 'M -1,0 0,-1 1,0 0,1 z';
+//        var myShape = circle;
+        var iconPath = vdsIconGreen;
+        if (color == 'red')
+        {
+           iconPath = vdsIconRed;
+        }
+        else if (color == 'yellow')
+        {
+            iconPath = vdsIconYellow;
+        }
         return {
-            path: myShape,
-            scale: 5,
-            strokeColor: "black", // the border color
-            strokeWeight: 1, // the border thickness
-            fillColor: color,
-            fillOpacity: 1.0
+//            path: iconPath,
+//            icon: 
+//                    {
+                        url: iconPath, 
+                        anchor: new google.maps.Point(6, 6)
+//                    };
+//            anchor: new google.maps.Point(6, 6),
+//            scale: 5,
+//            strokeColor: "black", // the border color
+//            strokeWeight: 1, // the border thickness
+//            fillColor: color,
+//            fillOpacity: 1.0
         };
     }
 
     // Load the map data from a json file and style all the points
-    function loadMapData()
+    function loadVDSlayer()
     {
         // Load the static map data and call saveCoords when done
         map.data.loadGeoJson(kMapStartupFile, null, saveCoords)
-
+//        var d = new Date();
+//        var start = d.getTime();
         // Style the map data by applying the desired properties to each feature (marker)
         // The function will be called every time a feature's properties are updated.
@@ -274,8 +275,13 @@
             var street = feature.getProperty("street");
             // Build the marker
-            var iconSymbol = dotSymbol(ptColor, name);
+            var iconSymbol = dotSymbol(ptColor);
             // return the StyleOptions
             return {
                 icon: iconSymbol,
+/*                    icon: 
+                    {
+                        url: vdsIconGreen, 
+                        anchor: new google.maps.Point(6, 6)
+                    }, */
                 title: name + " @" + street, // set rollover text
                 // set zIndex for slowed traffic to a higher value so they overlap
@@ -295,5 +301,5 @@
         });
         // update the dot colors from the dynamic json data 
-        updateMap();
+        updateVDSlayer();
         // go adjust the marker coordinates so dots don't overlap
         adjustCoords(calcDistanceFactor());
@@ -371,5 +377,5 @@
     }
     // Load the highways dynamic json file and update the map
-    function updateMap()
+    function updateVDSlayer()
     {
         var parsed_JSON;
@@ -483,5 +489,5 @@
     }
     // Initialize the view/hide buttons 
-    function initButton()
+    function initLayerButtons()
     {
         var cctvBtnDiv = document.getElementById('cctvButton');
@@ -493,8 +499,11 @@
             cctv_showing = !cctv_showing;
             // reveal or hide all the icons
-            for (var key in cctvList)
-            {
-                cctvList[key].setVisible(cctv_showing);
-            }
+            cctvLayer.forEach(function(feature)
+            {
+                cctvLayer.overrideStyle(feature,
+                {
+                    visible: cctv_showing
+                });
+            });
             // Determine which button image to show
             if (cctv_showing)
@@ -516,14 +525,10 @@
         {
             cms_showing = !cms_showing;
-            // reveal or hide all the icons
-            for (var key in cmsList)
-            {
-                //key = Object.keys(cmsList)[i];
-                cmsList[key].setVisible(cms_showing);
-            }
             // Determine which button image to show
             if (cms_showing)
             {
                 pic = "images/CPTMSImages/btnDepressed_CMS.png"
+                // It's nice when icons become visible that the messages have been refreshed.
+                loadAllMessages();
             }
             else
@@ -532,4 +537,12 @@
             }
             document.getElementById('cmsBtnImg').src = pic;
+            // reveal or hide all the icons
+            cmsLayer.forEach(function(feature)
+            {
+                cmsLayer.overrideStyle(feature,
+                {
+                    visible: cms_showing
+                });
+            });
         });
 
@@ -563,132 +576,92 @@
     }
 
-    function setCMSmarkers()
-    {
-        var simpleImage = "";
-        loadJSON(kCMSstartupFile, function(response)
-        {
-            // Parse JSON string into object
-            cms_info = JSON.parse(response);
-            //console.log(cms_info.data[0].cms);
-            // Process each new marker 
-            for (var i = 0; i < cms_info.data.length; i++)
-            {
-                var cms = cms_info.data[i].cms;
-                var currLat = Number(cms.location.latitude);
-                var currLong = Number(cms.location.longitude);
-                var cmsID = cms.index;
-                var directionCode = cms.location.direction.charAt(0);
-                var locationInfo = directionCode + " " + cms.location
-                    .route + " " + cms.location.postmile + " " + cms
-                    .location.locationName
-                cmsList[cmsID] = new google.maps.Marker(
-                {
-                    cmsid: cmsID,
-                    position:
-                    {
-                        lat: currLat,
-                        lng: currLong
-                    },
-                    map: map,
-                    icon: yellowFlag,
-                    title: "#" + cmsID + " " + locationInfo,
-                    location: locationInfo
-                });
-                cmsList[cmsID].setVisible(false); // initially hidden
-                google.maps.event.addListener(cmsList[cmsID], 'click',
-                    function()
-                    {
-                        var dialog = document.getElementById('dialog');
-                        dialog.style.display = 'block';
-                        // fetch the sequential msg #
-                        cmsID = this.cmsid;
-                        // Assign to the hidden field
-                        document.getElementById('cmsID').value = cmsID;
-                        showMessage(cmsID); // note: this is async
-                        document.getElementById('cms-info-label').innerHTML = "CMS ID: " +
-                            cmsID + "&nbsp;&nbsp;&nbsp;LOCATION: " + this.location;
-                        // clear input fields
-                        document.getElementById('msgcontent1').value = "";
-                        document.getElementById('msgcontent2').value = "";
-                        document.getElementById('msgcontent3').value = "";
-                        document.getElementById('msgcontent1').focus();
-                        var span = document.getElementsByClassName("close")[0]
-                            // When the user clicks on <span> (x), close the modal
-                        span.onclick = function()
-                        {
-                            handleClose();
-                        }
-                    });
-             }
-        });
-    }
-
-    function setCCTVmarkers()
-    {
-        loadJSON(kCCTVfile, function(response)
-        {
-            var imgTag = '<IMG WIDTH="700" SRC="images/CPTMSImages/';
-            // Parse JSON string into object
-            cctv_info = JSON.parse(response);
-            // Process each new marker 
-            for (var i = 0; i < cctv_info.data.length; i++)
-            {
-                var cctv = cctv_info.data[i].cctv;
-                var currLat = Number(cctv.location.latitude);
-                var currLong = Number(cctv.location.longitude);
-                var locationInfo = cctv.location.locationName;
-                var imgIcon = cctvIcon
-                if ((typeof map.data.getFeatureById(cctv.location.nearVDS)) == "undefined")
-                {
-                    imgIcon = cctvIconWhite;
-                }
-
-                var vdsResult = map.data.getFeatureById(cctv.location.nearVDS)
-                    //console.log("building "+locationInfo+" near "+cctv.location.nearVDS + " found "+vdsResult); 
-                cctvList[i] = new google.maps.Marker(
-                {
-                    position:
-                    {
-                        lat: currLat,
-                        lng: currLong
-                    },
-                    map: map,
-                    icon: imgIcon,
-                    title: "#" + i + " " + locationInfo,
-                    cctvid: "" + i,
-                    location: locationInfo,
-                    index: cctv.index,
-                    nearVDS: cctv.location.nearVDS
-                });
-                cctvList[i].info = new google.maps.InfoWindow(
-                {
-                    content: locationInfo
-                });
-
-                cctvList[i].setVisible(false); // initially hidden
-                cctvList[i].addListener('click',
-                    function()
-                    {
-                        cctvIndex = this.index;
-                        //console.log(this.title + " is looking for " + this.nearVDS);
-                        currentFeature = map.data.getFeatureById(this.nearVDS);
-                        currentColor = currentFeature.getProperty("color");
-                        var imgDir = "CCTVFast/";
-                        if (currentColor == "red" || currentColor == "yellow")
-                        {
-                            imgDir = "CCTVSlow/"
-                        }
-                        //console.log(currentFeature.getId() +  ' ' + currentColor + " " + cctvIndex);
-                        this.info.setContent('<div style="font-weight:bold;font-family: monospace">' + this.location + "&nbsp;nearVDS:" +
-                            this.nearVDS + "&nbsp;" + currentColor + "<BR>" + imgTag + imgDir + cctvIndex + '.jpg">' + "</div>");
-                        this.info.open(map, this);
-                    });
-            }
-        });
-    }
-
+function loadCMSlayer()
+{
+    cmsLayer = new google.maps.Data();
+    cmsLayer.setMap(map);
+    cmsLayer.loadGeoJson("cms_locations_D12.gjson");
+    cmsLayer.setStyle(function(feature)
+    {
+        // return the StyleOptions
+        return {
+            icon: yellowFlag,
+            title: feature.getId()+ " " +feature.getProperty("location"),
+            visible: false
+        };
+    });
+    
+    cmsLayer.addListener('click', function(event)
+    {
+        var dialog = document.getElementById('dialog');
+        // Note: If the dialog is already being displayed when someone else
+        // updates the message, it won't be reflected in the dialog, until
+        // you close and reopen it.
+        dialog.style.display = 'block';
+        cmsID = event.feature.getId();
+        // Assign to the hidden field
+        document.getElementById('cmsID').value = cmsID;
+        showMessage(cmsID); // note: this is async
+        document.getElementById('cms-info-label').innerHTML = "CMS ID: " +
+            cmsID + "&nbsp;&nbsp;&nbsp;LOCATION: " + event.feature.getProperty("location");
+        // clear input fields
+        document.getElementById('msgcontent1').value = "";
+        document.getElementById('msgcontent2').value = "";
+        document.getElementById('msgcontent3').value = "";
+        document.getElementById('msgcontent1').focus();
+        var span = document.getElementsByClassName("close")[0]
+            // When the user clicks on <span> (x), close the modal
+        span.onclick = function()
+        {
+            handleDialogClose();
+        }
+    });
+}
+function makecctvIcon(nearVDS)
+{
+    var imgIcon = cctvIcon
+    if ((typeof map.data.getFeatureById(nearVDS)) == "undefined")
+    {
+        imgIcon = cctvIconWhite;
+    }
+    return imgIcon
+}
+function loadCCTVlayer()
+{
+    var imgTag = '<IMG WIDTH="700" SRC="images/CPTMSImages/';
+    cctv_infowindow = new google.maps.InfoWindow();
+    cctvLayer = new google.maps.Data();
+    cctvLayer.loadGeoJson("cctv_locations_D12.gjson");
+    cctvLayer.setStyle(function(feature)
+    {
+        // return the StyleOptions
+        return {
+            icon: makecctvIcon(feature.getProperty("nearVDS")),
+            title: feature.getId() + " " +feature.getProperty('locationName'),
+            visible: false  
+        };
+    });
+    cctvLayer.addListener('click', function(event)
+    {
+        //console.log("cctv layer was clicked at " + event.feature.getId());
+        cctvIndex = event.feature.getId();
+        //console.log(this.title + " is looking for " + this.nearVDS);
+        nearVDS = map.data.getFeatureById(event.feature.getProperty("nearVDS"));
+        currentColor = nearVDS.getProperty("color");
+        var imgDir = "CCTVFast/";
+        if (currentColor == "red" || currentColor == "yellow")
+        {
+            imgDir = "CCTVSlow/"
+        }
+
+        cctv_infowindow.setContent('<div style="font-weight:bold;font-family: monospace">' +  nearVDS.getId() + "&nbsp;" + currentColor + "<BR>" + imgTag + imgDir + cctvIndex + '.jpg">' + "</div>");
+        cctv_infowindow.setPosition(event.feature.getGeometry().get());
+        cctv_infowindow.open(map);
+
+    });
+    cctvLayer.setMap(map);
+}
 
     // Center justify message text in a 16 column field
-    function justifyText(message)
+    function justifyCMStext(message)
     {
         var kBlanks = "                ";
@@ -698,10 +671,10 @@
     }
 
-    function handleSubmit()
+    function handleCMSsubmit()
     {
         // recover the user's response
-        var response1 = justifyText(document.getElementById('msgcontent1').value.trim());
-        var response2 = justifyText(document.getElementById('msgcontent2').value.trim());
-        var response3 = justifyText(document.getElementById('msgcontent3').value.trim());
+        var response1 = justifyCMStext(document.getElementById('msgcontent1').value.trim());
+        var response2 = justifyCMStext(document.getElementById('msgcontent2').value.trim());
+        var response3 = justifyCMStext(document.getElementById('msgcontent3').value.trim());
         var newMsg = response1 + response2 + response3;
         if (newMsg.length == 0)
@@ -718,5 +691,5 @@
     }
 
-    function handleClose()
+    function handleDialogClose()
     {
         // hide the display
@@ -724,5 +697,5 @@
     }
 
-    function handleClear()
+    function handleCMSclear()
     {
         document.getElementById('msgdisplay1').value = "";
@@ -753,36 +726,40 @@
                 var item = messagejson.data[i];
                 messageDict[item.cms.index] = item;
+                // Set the appropriate icon on the cms icon
+                // set a yellow flag if there's currently no message
+                if (item.cms.message.phase1.Line1 + 
+                    item.cms.message.phase1.Line2 +
+                    item.cms.message.phase1.Line3 == "")
+                {
+                    cmsLayer.overrideStyle(cmsLayer.getFeatureById(item.cms.index), {icon: yellowFlag})
+                }
+                else
+                {
+                    cmsLayer.overrideStyle(cmsLayer.getFeatureById(item.cms.index), {icon: blueFlag})
+                }
             }
         });
     }
-    function refreshCMSicons()
-    {
-        loadAllMessages();
-        // Examine each CMS's message
-        for (var idx in cmsList)
-        {
-            // load a yellow flag if there's currently no message
-            if (messageDict[idx].cms.message.phase1.Line1 + 
-                messageDict[idx].cms.message.phase1.Line2 +
-                messageDict[idx].cms.message.phase1.Line3 == "")
-                cmsList[idx].setIcon(yellowFlag);
-            else
-                cmsList[idx].setIcon(blueFlag);
-        }
-    }
+
     // Save an updated cms message to the file
-    function saveMessage(outMessage, cmsID)
-    {
+    function saveMessage(outMessage)
+    {
+        // Fetch cmsID from hidden field where it was put when dialog opened.
         var cmsID = document.getElementById('cmsID').value;
-        console.log("Saving " + outMessage + " for cmsID " + cmsID)
+        //console.log("Saving " + outMessage + " for cmsID " + cmsID)
         msgParts = outMessage.split("|");
         messageDict[cmsID].cms.message.phase1.Line1 = msgParts[0];
         messageDict[cmsID].cms.message.phase1.Line2 = msgParts[1];
         messageDict[cmsID].cms.message.phase1.Line3 = msgParts[2];
-        // Change icon if something was saved
+        // Set icon to reflect message state
         if (outMessage == "||")
-            cmsList[cmsID].setIcon(yellowFlag);
+        {
+            currentIcon = {icon: yellowFlag};
+        }
         else
-            cmsList[cmsID].setIcon(blueFlag);
+        {
+            currentIcon = {icon: blueFlag};
+        }
+        cmsLayer.overrideStyle(cmsLayer.getFeatureById(cmsID), currentIcon)
         outString = "{\n\t\"data\":\n\t\t" + JSON.stringify(Object.values(messageDict)) + "}";
 
@@ -810,17 +787,19 @@
             streetViewControl: false
         });
-        loadMapData(); // go load the map data
+        loadVDSlayer(); // go load the map data
         // setup the search box and center button
         initSearch();
         initCenter();
-        loadAllMessages(); // load the current message file
-        var startTime = setTimeout(setCMSmarkers, 2000);
-        var startTime = setTimeout(setCCTVmarkers, 3000);
-        initButton();
-
-        // Start a timer to refresh the map every 10 seconds
-        var myTimer = setInterval(updateMap, 10000);
+
+loadCMSlayer();
+
+loadCCTVlayer();
+
+        initLayerButtons();
+
+        // Start a timer to refresh the vds dots every 10 seconds
+        var myTimer = setInterval(updateVDSlayer, 10000);
         // start an interval timer to refresh the cms icons every 10 seconds
-        var cmsTimer = setInterval(refreshCMSicons, 10000);
+        var cmsTimer = setInterval(loadAllMessages, 10000);
         // Listen for zoom changes and move the placePins so as to keep a nice
         // visual distance between them appropriate to the zoom factor
