// Load the map data from a json file and style all the points function initializeVDSlayer() { // 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] }; }); } // 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) }; } // 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 }); // go adjust the marker coordinates so dots don't overlap adjustCoords(calcDistanceFactor()); processVDS(); } // 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); 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]); } } // Load the highways static json file and update the map function resetVDSlayer() { eventIndex = -1; loadJSON(kMapStartupFile, function(response) { // Parse JSON string into object //initialVDSjson = JSON.parse(response); // Process each new marker - lookup in current map var initialVDSjsonFeatures = JSON.parse(response).features initialVDSjsonFeatures.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"; } // Parses the traffic events file and builds an array of target dots for each line function processVDS() { //var parsed_JSON; loadJSON(kMapStartupFile, 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; parsed_features.forEach(updateMarker); var eventsFile = ''; // Request the traffic events data file var xmlhttp = new XMLHttpRequest(); xmlhttp.open("GET",trafficEventsFile,true); // Ask the server for the traffic events file // Establish listener for handling the traffic events once file is read xmlhttp.onreadystatechange = function() { if(xmlhttp.status == 200 && xmlhttp.readyState == 4) { eventsFile = xmlhttp.responseText; // Process the traffic events file var lines = eventsFile.toString().split("\n"); // For each line in the events file for (var line = 1; line < lines.length; line++) { var trimmedLine = lines[line].trim(); if (trimmedLine[0] !== '#') //ignores lines with # { var dots = []; var event = trimmedLine.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]); // extract the event time // Process each VDS in the highway network parsed_features.forEach(function (marker) { var marker_fields = marker.id.split(" "); // See if this marker's highway and direction match to this event if (marker_fields[0] === event[2] && marker_fields[1] === event[3]) { //computes difference in postmiles var dist = parseFloat(marker_fields[2]) - start; // If this marker is within computed range if (dist <= range && dist >= 0) { // Add the marker to the dots representing this event dots.push({"marker": marker, "color": newColor}); } } }); targetDots.push(dots); // add this events dots to the list of targetdots } // end if } // end for // After the traffic events are processed, go build the differences array buildDiff(); // side effect: leaves map with last event showing resetVDSlayer(); // restore the map to initial state } }; xmlhttp.send(); }); } // Calls updateMarker for all targetDots after storing the previous marker in diff_arr // This function only needs to be done once, during startup. 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); } } } // Helper function for BuildDiff // Store the previous colors of the targetDots // so it will available if the user goes "back". 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}}); } } // 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; } } // init beginning, forward and back buttons as well as time display on map function initControlButtons() { var i = 0; // setup the time display var time = document.getElementById('time'); map.controls[google.maps.ControlPosition.BOTTOM_CENTER].push(time) // set up the "beginning" button var beginning = document.getElementById('beginning'); map.controls[google.maps.ControlPosition.BOTTOM_CENTER].push(beginning) beginning.title = 'Click to see the first event'; // set up the "next" button var forward = document.getElementById('forward'); map.controls[google.maps.ControlPosition.BOTTOM_CENTER].push(forward) // set up the "back" button var backward = document.getElementById('backward'); map.controls[google.maps.ControlPosition.BOTTOM_CENTER].push(backward) forward.title = 'Click to see the next event'; //console.log(targetDots, eventTimes); // Establish the listeners for each button 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; } }); beginning.addEventListener('click', function() { resetVDSlayer(); // redraw markers on map from startup file i = 0; time.innerHTML = "00:00:00"; }); }