var postmileList = []; // the list of postmiles saved from the json file. var chosenColor = null; Array.prototype.diff = function(a) { return this.filter(function(i) {return a.indexOf(i) < 0;}); }; // Build a solid colored icon to use instead of the classic pin function dotSymbol(color) { var iconPath = iconVDSwhite; if (color == 'red') { iconPath = iconVDSred; } else if (color == 'yellow') { iconPath = iconVDSyellow; } else if (color == 'lime') { iconPath = iconVDSgreen; } return { url: iconPath, anchor: new google.maps.Point(6, 6) }; } // Load the map data from a json file and style all the points function loadVDSlayer() { // Load the static map data and call saveCoords when done map.data.loadGeoJson(kMapStartupFile, null, saveCoords) // 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. map.data.setStyle(function(feature) { // Get the postmile id var name = feature.getId(); // Get the desired color value var ptColor = feature.getProperty("color"); var street = feature.getProperty("street"); // Build the marker var iconSymbol = dotSymbol(ptColor); // return the StyleOptions return { icon: iconSymbol, title: name + " @" + street, // set rollover text // set zIndex for slowed traffic to a higher value so they overlap zIndex: colorZvalues[ptColor] }; }); var clicked = false; var first = []; var asc = false; var color_arr = []; var white_arr = []; map.data.addListener('click', function(event) { var event_arr = event.feature.getId().split(/[ ,]+/); // var start = parseFloat(event[4]); // extract postmile field if (clicked && event_arr[0] === first[0] && event_arr[1] === first[1]) { // console.log('clicked ' + first + "/" + event_arr + event.feature); var range = Math.abs(parseFloat(event_arr[2]) - parseFloat(first[2])); var clicked_dots = []; if (parseFloat(event_arr[2]) < parseFloat(first[2])) { asc = false; } else { asc = true; } map.data.forEach(function (marker) { var marker_arr = marker.getId().split(/[ ,]+/); if (marker_arr[0] === event_arr[0] && marker_arr[1] === event_arr[1]) { if (asc) { var dist = parseFloat(marker_arr[2]) - parseFloat(first[2]); } else { var dist = parseFloat(parseFloat(first[2]) - (marker_arr[2])); } if (dist <= range && dist >= 0) { clicked_dots.push(marker); } } }); color_arr.push(clicked_dots); console.log(clicked_dots); console.log(range); var white_arr_ids = white_arr.map(function (marker) { return marker.id; }); var clicked_dots_ids = clicked_dots.map(function (marker) { return marker.getId(); }); var unused_ids = white_arr_ids.diff(clicked_dots_ids); console.log("Unused: ",unused_ids, "White: ",white_arr_ids); unused_ids.forEach(function (id){ var marker = white_arr.filter(function (dot) { return dot.id === id; })[0]; var feature = map.data.getFeatureById(marker.id); feature.setProperty("color", marker.color); }); white_arr = []; if (chosenColor) { clicked_dots.forEach(function (dot) { dot.setProperty("color", chosenColor); }); } // Assemble the line to be written to the events file var lineOut = "181 00:00:00 " + first[0] + " " + first[1] + " " + first[2] + " " + range.toFixed(3) + " " + getColorAbbr(chosenColor); // Using Ajax POST to send the data var xhr = new XMLHttpRequest(); xhr.open("POST", "../../cgi-bin/saveTrafficEvent.py", true); xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8'); // send the collected data xhr.send("msg="+lineOut + "\n") } else if (!clicked) { first = event_arr; // turns relevant dots white selectedID = event.feature.getId(); // get the id of the clicked dot curr = postmileList.indexOf(selectedID); // find the id in the postmile list var firstBlank = postmileList[curr].indexOf(" ", 0); // find direction substring var secondBlank = firstBlank+2; var direction = postmileList[curr].charAt(firstBlank+1); // extract direction target = event.feature.getId().substring(0,secondBlank); // extract target string console.log("making white dots from " + selectedID + " in direction "+direction) // Decide whether to increment or decrement based on direction of hwy; // N and E increment, S and W decrement if (direction == 'N' || direction == 'E') { // Process all dots with same hwy and direction while (postmileList[curr].startsWith(target) && curr < postmileList.length) { // turn the dot white currFeat = map.data.getFeatureById(postmileList[curr]); white_arr.push({id : currFeat.getId(), color : currFeat.getProperty("color")}); currFeat.setProperty("color", "white"); curr++; } } else { // Process all dots with same hwy and direction while (postmileList[curr].startsWith(target) && curr > 0) { currFeat = map.data.getFeatureById(postmileList[curr]); white_arr.push({id : currFeat.getId(), color : currFeat.getProperty("color")}); // turn the dot white currFeat.setProperty("color", "white"); curr--; } } } else { console.log('wrong choice'); } clicked = !clicked; }); } // callback when load GeoJson completes // save each feature's Point as the original coordinates for later reference function saveCoords(features) { // Iterate over all the features in the map features.forEach(function(feature) { var pt = feature.getGeometry().get(); vds_coords[feature.getId()] = pt; // save the Point in a dictionary postmileList.push(feature.getId()); // save the postmile in a list }); // update the dot colors from the dynamic json data updateVDSlayer(); // go adjust the marker coordinates so dots don't overlap adjustCoords(calcDistanceFactor()); } // magic formula controls distance between dots proportionate to zoom factor function calcDistanceFactor() { // 15 is maximum zoom, the point at which no adjustment is needed return (.0005 * (15 - map.getZoom())); } // Adjust the coordinates of dots so they appear side-by-side // The perpendicular vector for each dot has been provided, // so we just need to multiply by a scaling factor (adjAmount) // @param adjAmount amount by which to adjust coordinate function adjustCoords(adjAmount) { // Adjust the NB points a slight amount map.data.forEach(function(feature) { // get the name of the current feature var name = feature.getId(); // lookup the original coordinates for this feature var coords = vds_coords[name]; //retrieve the perpendicular vector (precomputed) var perpx = feature.getProperty("perpx") var perpy = feature.getProperty("perpy") // Make adjustment and save it var myLat = coords.lat() + perpy * adjAmount var myLong = coords.lng() + perpx * adjAmount feature.setGeometry( { lat: myLat, lng: myLong }); }); } // update the color (as needed) for a given marker function updateMarker(marker) { target = marker.id; newColor = marker.properties.color; // see if new color is different than current color currentFeature = map.data.getFeatureById(target); if (currentFeature) { currentColor = currentFeature.getProperty("color"); // if a new color is desired then assign it to the feature's color property if (currentColor != newColor) { currentFeature.setProperty("color", newColor); // set zIndex for slowed traffic to a higher value so they overlap currentFeature.setProperty("zIndex", colorZvalues[newColor]); } } } // Builds array to store the previous colors of the targetDots /*function storePrev(marker, targetMarkers) { target = marker.id; newColor = marker.properties.color; // see if new color is different than current color currentFeature = map.data.getFeatureById(target); currentColor = currentFeature.getProperty("color"); // if a new color is desired then assign it to the feature's color property if (currentColor != newColor) { targetMarkers.push({"id" : currentFeature.getId(), "properties" : {"color" : currentColor}}); } }*/ // Load the highways dynamic json file and update the map function updateVDSlayer() { eventIndex = -1; var parsed_JSON; loadJSON(kVDSstatusFile, function(response) { // Parse JSON string into object parsed_JSON = JSON.parse(response); // Process each new marker - lookup in current map parsed_JSON.features.forEach(updateMarker); }); } function getColorName(str){ if (str === "R\r" || str === "R") return "red"; else if (str === "Y\r" || str === "Y") return "yellow"; else if (str === "G\r" || str === "G") return "lime"; } function getColorAbbr(str){ if (str === "red") return "R"; else if (str === "yellow") return "Y"; else if (str === "lime") return "G"; } // Parses the traffic events file and builds an array of target dots for each line /*function processVDS() { var parsed_JSON; loadJSON(kVDSstatusFile, function(response) { // Parse JSON string into object parsed_JSON = JSON.parse(response); // Process each new marker - lookup in current map var parsed_features = parsed_JSON.features; var eventsFile = ''; var xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = function(){ if(xmlhttp.status == 200 && xmlhttp.readyState == 4){ eventsFile = xmlhttp.responseText; var lines = eventsFile.toString().split("\n"); for (var line = 1; line < lines.length; line++) { if (lines[line][0] !== '#') //ignores lines with # { var dots = []; var event = lines[line].split(/[ ,]+/); var start = parseFloat(event[4]); // extract postmile field var range = parseFloat(event[5]); // extract distance var newColor = getColorName(event[6]); // extract DotColor eventTimes.push(event[1]); parsed_features.forEach(function (marker) { var id_arr = marker.id.split(" "); if (id_arr[0] === event[2] && id_arr[1] === event[3]){ var dist = parseFloat(id_arr[2]) - start; //computes difference in postmiles if (dist <= range && dist >= 0){ // adds all dots within computed range dots.push({"marker": marker, "color": newColor}); } } }); targetDots.push(dots); } } } }; xmlhttp.open("GET",trafficEventsFile,true); //read traffic events text file xmlhttp.send(); }); } // Increments the eventIndex from and updates all the targetDots on map function updateForwards() { eventIndex++; // limit eventIndex to length of targetDots if (eventIndex >= targetDots.length) { eventIndex = targetDots.length - 1; } // update all the target dots if (targetDots[eventIndex] !== undefined) { targetDots[eventIndex].forEach(function(item){ item.marker.properties.color = item.color; updateMarker(item.marker); }); } } // Decrements the eventIndex and updates all the targetDots from diff_arr on map function updateBackwards() { //update each target dot in order to revert to previous color if (diff_arr[eventIndex] !== undefined) { diff_arr[eventIndex].forEach(function(item){ updateMarker(item); }); } eventIndex--; // restrict eventIndex to -1 if (eventIndex < -1) { eventIndex = -1; } } // Calls updateMarker for all targetDots after storing the previous marker in diff_arr function buildDiff() { for (var i = 0; i <= targetDots.length; i++) { if (targetDots[i] !== undefined) { var targetMarkers = new Array(); targetDots[i].forEach(function(item){ item.marker.properties.color = item.color; storePrev(item.marker, targetMarkers); updateMarker(item.marker); }); diff_arr.push(targetMarkers); } } } */ /*function initVDSbutton() { var vdsBtnDiv = document.getElementById('vdsButton'); map.controls[google.maps.ControlPosition.LEFT_BOTTOM].push(vdsBtnDiv) vdsBtnDiv.title = 'Click to toggle vds view'; // Setup the click event listeners to toggle icon display vdsBtnDiv.addEventListener('click', function() { vds_showing = !vds_showing; // reveal or hide all the dots map.data.forEach(function(feature) { map.data.overrideStyle(feature, { visible: vds_showing }); }); // Determine which button image to show if (vds_showing) { pic = "images/btnDepressed_VDS.png" } else { pic = "images/btnReady_VDS.png" } document.getElementById('vdsBtnImg').src = pic; }); }*/ function initColorButtons() { var redColor = document.getElementById('redButton'); var greenColor = document.getElementById('greenButton'); var yellowColor = document.getElementById('yellowButton'); map.controls[google.maps.ControlPosition.BOTTOM_CENTER].push(redColor); map.controls[google.maps.ControlPosition.BOTTOM_CENTER].push(greenColor); map.controls[google.maps.ControlPosition.BOTTOM_CENTER].push(yellowColor); redColor.addEventListener('click', function() { chosenColor = "red"; }); greenColor.addEventListener('click', function() { chosenColor = "lime"; }); yellowColor.addEventListener('click', function(){ chosenColor = "yellow"; }); } // init beginning, forward and back buttons as well as time display on map /*function initControlButtons() { var i = 0; var time = document.getElementById('time'); map.controls[google.maps.ControlPosition.BOTTOM_CENTER].push(time) var start = document.getElementById('start'); map.controls[google.maps.ControlPosition.BOTTOM_CENTER].push(start) start.title = 'Click to see the first event'; var forward = document.getElementById('forward'); map.controls[google.maps.ControlPosition.BOTTOM_CENTER].push(forward) var backward = document.getElementById('backward'); map.controls[google.maps.ControlPosition.BOTTOM_CENTER].push(backward) forward.title = 'Click to see the next event'; forward.addEventListener('click', function() { updateForwards(); backward.disabled = false; if (i < eventTimes.length - 1) // get the next eventTime { ++i; time.innerHTML = eventTimes[i]; } if (eventIndex === targetDots.length - 1) // disable next button if last event reached { forward.disabled = true; } }); backward.addEventListener('click', function() { updateBackwards(); forward.disabled = false; if (i > 0) // get the prev eventTime { --i; time.innerHTML = eventTimes[i]; } if (eventIndex < 0) // disable back button if at first event { backward.disabled = true; } }); start.addEventListener('click', function() { updateVDSlayer(); // redraw markers on map from startup file i = 0; time.innerHTML = "00:00:00"; }); } */