Index: trunk/webapps/cameradisplay/index.html
===================================================================
--- trunk/webapps/cameradisplay/index.html	(revision 682)
+++ trunk/webapps/cameradisplay/index.html	(revision 682)
@@ -0,0 +1,262 @@
+<html>
+<head>
+<meta charset="UTF-8">
+<title>CPTMS Camera Display Controller V1</title>
+<style>
+.toprow {
+  text-align: center
+}
+td {
+  border: 1px solid black;
+}
+img {  /* confine the image to the cell dimensions */
+    max-width: 100%;
+    max-height: 100%;
+}
+.caption {
+    font-family: sans-serif;
+    text-align: center
+}
+</style>
+<script  src="../common/js/fileutils.js"></script>
+<script>
+/* Camera Controller for CPTMS
+ * @author jdalbey   Apr 2020
+ */
+var kVDSstatusFile = "../dynamicdata/highway_status.json"; // dynamic json data file 
+var kCCTVfile = "../cptms/data_layers/cctv_locations_D12.gjson"; // CCTV locations
+var vdsList;
+var cctvList;
+var routeCameras = {};  // for each route, a list of cameras on that route
+var cameraDict = {};    // for each cameraid, the associated nearVDS and locationName properties
+
+// Extracts VDS and CCTV data and builds a lookup dictionary that maps route to camera id's and adds routes to drop down list
+function init()
+{
+    loadJSON(kVDSstatusFile, function(response)
+    {
+        // Parse JSON string into list of VDS's
+        vdsList = JSON.parse(response);
+
+    });
+    loadJSON(kCCTVfile, function(response)
+    {
+        // Parse JSON string into list of cctv's
+        cctvList = JSON.parse(response);
+        cctvList.features.forEach(buildDict);
+        // Build the route dropdowns using routeCameras keys
+        for (var quadrant = 1; quadrant <= 4; quadrant++)
+        {
+            routecombo = document.getElementById("R"+quadrant)
+            removeOptions(routecombo);
+            routecombo.add(document.createElement('option')); // an empty entry
+            // Get all the routes and sort them
+            var routeList =  Object.keys(routeCameras);
+            routeList.sort()
+            // Add all the routes to the route dropdown box
+            for (var route of routeList)
+            {
+                opt1 = document.createElement('option')
+                opt1.text = opt1.value = route
+                routecombo.add(opt1);
+            }
+        }
+    });
+
+    // Start a timer to refresh the traffic colors every 30 seconds
+    var myTimer = setInterval(updateVDSlist, 30000);
+}
+
+// Takes in a CCTV location and builds a lookup dictionary that maps route to camera id's
+function buildDict(cctvItem)
+{
+    id = cctvItem.id
+    route = id.substring(3,6); // extract 3 character route number
+    if (route in routeCameras)
+    {
+        routeCameras[route].push(id); // add the camera id to the list for the route
+    }
+    else
+    {
+        routeCameras[route] = [id]; // special case for first occurrence of route
+    }
+    // Add a camera's info to the cameraDict
+    cameraDict[id] = {'nearVDS':cctvItem.properties["nearVDS"],
+                      'locationName':cctvItem.properties["locationName"]}
+}
+
+/* When a route is selected from the combobox, filter the
+   list of cameras for just those on that route. */
+function routechanged(routechoice, cameraselect) 
+{
+  // get the selected route
+  var e = document.getElementById(routechoice);
+  var currentRoute = e.options[e.selectedIndex].text;
+  // update the list of cameras
+  removeOptions(document.getElementById(cameraselect));  
+  fillOptions(currentRoute, cameraselect);
+  showView(cameraselect); //show default first image from camera dropdown for the selected route
+}
+
+// Helper: Remove all the options from a combo box
+function removeOptions(selectbox)
+{
+    var idx;
+    for(idx = selectbox.options.length - 1 ; idx >= 0 ; idx--)
+    {
+       selectbox.remove(idx);
+    }
+}
+
+// Fill the selectbox with items from the lookup table that match route
+// route param may be empty if the first item in the combo box was chosen
+function fillOptions(route,cameraselect)
+{
+  // IF the route is not empty
+  if (route.length > 0)
+  {
+      var cameracombo = document.getElementById(cameraselect)
+      // grab cameras from lookup table
+      cameranames = routeCameras[route];
+      //console.log(cameranames.length + " cameras for route " + route);
+      // Create a new select OPTION for each camera
+      for (var idx = 0; idx < cameranames.length; idx++)
+      {
+        var opt1 = document.createElement('option')
+        opt1.value = cameranames[idx]
+        opt1.text = cameraDict[cameranames[idx]].locationName
+        cameracombo.add(opt1);
+      }
+   } 
+}
+
+// Display the image requested from a camera dropdown box
+function showView(cameraselect)
+{
+    var quadrant = "";
+    // if route dropdown box is selected, then view image
+    if (typeof cameraselect === 'string')
+    {
+      quadrant = cameraselect.charAt(1)  // extract numeric part of camera identifier
+      var chosenRoute = document.getElementById('R' + quadrant);
+      var imgElement = document.getElementById("img"+quadrant)    
+      var e = document.getElementById(cameraselect);
+      // if no camera was selected
+      if (e.selectedIndex == -1)
+      {
+          imgElement.src="" // remove the image
+      }
+      else
+      {
+          var chosenCamera = e.options[e.selectedIndex].value;
+          nearvds = cameraDict[chosenCamera].nearVDS
+          // Search for the vds that is nearest
+          var idx = 0; 
+          while (idx < vdsList.features.length && vdsList.features[idx].id != nearvds)
+          {
+              idx++;
+          }
+          // If we found the nearVDS
+          if (idx < vdsList.features.length)
+          {
+              // Obtain color and convert to speed
+              var foundVDS = vdsList.features[idx]
+              var color =  foundVDS.properties['color']
+              var speed = "freeflow"
+              if (color == "yellow")
+              {
+                  speed = "slow"
+              }
+              if (color == "red")
+              {
+                  speed = "stopped"
+              }
+              // construct filename
+              var filename =  chosenCamera + "-day-"+speed+".jpg"
+              //console.log("Found " + foundVDS.id + " " + foundVDS.properties['color'] + " " + filename)
+              // Load the desired image
+              var preload = new Image();
+              preload.onload = function() {
+                  if (imgElement) 
+                  { 
+                      imgElement.src = preload.src; // image
+                      imgElement.title=chosenCamera // tooltip
+                  }
+              };
+              // if couldn't load show as unavailable
+              preload.onerror = function() {
+                  if (imgElement) { imgElement.src = "../cptms/images/CCTV/video_unavailable.jpg" } 
+              };
+              // attempt to load the image
+              preload.src = "../cptms/images/CCTV/"+filename;  
+      //Reference: https://www.daniweb.com/programming/web-development/threads/272293/javascript-test-for-file-existence
+          }
+      }
+    }
+   //console.log("empty");
+}
+// Execute periodically to reload the vds list with current data
+function updateVDSlist()
+{
+    loadJSON(kVDSstatusFile, function(response)
+    {
+        // Reload the vds list with current data
+        vdsList = JSON.parse(response);
+        // Refresh each camera view
+        var views = document.getElementsByClassName("camcombo");
+        for (var quadrant of views)
+        {
+            //console.log(quadrant.id);
+            showView(quadrant.id);
+        }
+    });
+    
+}
+</script>
+</head>
+<body onload="init()">
+<div style="text-align: center; width:80%">
+CPTMS Camera Controller
+</div>
+<table width="80%">
+  <tr>
+    <td class="caption" width="50%"><img id="img1" src=""/><br>
+  Choose Route
+  <select id="R1" onchange='routechanged("R1","C1")'>
+    <option value="0"></option>
+  </select>
+  <br>Choose Camera
+  <select id="C1" class="camcombo" onchange='showView("C1")'>
+  </select></td>
+    <td class="caption"><img  id="img2"src=""/><br>
+  Choose Route
+  <select id="R2" onchange='routechanged("R2","C2")'>
+    <option value="0"></option>
+  </select>
+  <br>Choose Camera
+  <select id="C2" class="camcombo" onchange='showView("C2")'>
+  </select></td>
+  </tr>
+  <tr>
+    <td class="caption"><img id="img3" src=""/><br>
+  Choose Route
+  <select id="R3"  onchange='routechanged("R3","C3")'>
+    <option value="0"></option>
+  </select>
+  <br>Choose Camera
+  <select id="C3" class="camcombo" onchange='showView("C3")'>
+  </select>
+</td>
+    <td class="caption"><img id="img4" src=""/><br>
+  Choose Route
+  <select id="R4" onchange='routechanged("R4","C4")'>
+    <option value="0"></option>
+  </select>
+  <br>Choose Camera
+  <select id="C4" class="camcombo" onchange='showView("C4")'>
+</select>
+</td>
+  </tr>
+</table>  
+</body>
+</html>
