source: tmcsimulator/trunk/webapps/GTEC/js/vdsLayer.js @ 579

Revision 579, 15.1 KB checked in by jdalbey, 6 years ago (diff)

Add timebox to GTEC.

Line 
1/** Logic for traffic events creator.
2    Based on vdsLayer from CPTMS.
3    @author T. Kumar, J. Dalbey   Feb 2020
4    See wiki for user documentation.
5*/
6var postmileList = [];  // the list of postmiles saved from the json file.
7var chosenColor = "yellow"; // stores color that user picks, initial default yellow
8var currentTime; // the current event time
9
10// function to find differences between two arrays
11Array.prototype.diff = function(a) {
12    return this.filter(function(i) {return a.indexOf(i) < 0;});
13};
14
15    // Build a solid colored icon to use instead of the classic pin
16    function dotSymbol(color) 
17    {
18        var iconPath = iconVDSwhite;
19        if (color == 'red')
20        {
21           iconPath = iconVDSred;
22        }
23        else if (color == 'yellow')
24        {
25            iconPath = iconVDSyellow;
26        }
27        else if (color == 'lime')
28        {
29            iconPath = iconVDSgreen;
30        }
31        return {
32            url: iconPath, 
33            anchor: new google.maps.Point(6, 6)
34        };
35    }
36
37    function saveTime() 
38    {
39        var timeOverride = document.getElementById("timebox").value; 
40        var d = new Date(); // creates a Date Object using the clients current time
41
42        let [hours,minutes,seconds] = timeOverride.split(':'); // using ES6 destructuring
43
44        d.setHours(+hours); // set the hours, using implicit type coercion
45        d.setMinutes(minutes); // you can pass Number or String, it doesn't really matter
46        d.setSeconds(seconds);
47        currentTime = d;  // save as new current time
48    }
49
50    // Refresh the time display
51    function updateTime(time)
52    {
53        document.getElementById("timebox").value = time;   
54    }
55
56    // Load the map data from a json file and style all the points
57    function loadVDSlayer()
58    {
59        // Load the static map data and call saveCoords when done
60        map.data.loadGeoJson(kMapStartupFile, null, saveCoords)
61        // Style the map data by applying the desired properties to each feature (marker)
62        // The function will be called every time a feature's properties are updated.
63        map.data.setStyle(function(feature)
64        {
65            // Get the postmile id
66            var name = feature.getId();
67            // Get the desired color value
68            var ptColor = feature.getProperty("color");
69            var street = feature.getProperty("street");
70            // Build the marker
71            var iconSymbol = dotSymbol(ptColor);
72            // return the StyleOptions
73            return {
74                icon: iconSymbol,
75                title: name + " @" + street, // set rollover text
76                // set zIndex for slowed traffic to a higher value so they overlap
77                zIndex: colorZvalues[ptColor]
78            };
79        });
80        var clicked = false; 
81        var first = []; // stores first dot
82        var asc = false; //ascending
83        var white_arr = []; //stores all white dots
84        // Initialize the current time to zero
85        currentTime = new Date(0);
86        currentTime.setHours(0);
87        currentTime.setMinutes(0);
88        currentTime.setSeconds(0);
89
90        map.data.addListener('click', function(event) 
91        {
92            var event_arr = event.feature.getId().split(/[ ,]+/);
93            // if user has already made the first click and the second click is
94            // on the same highway and same direction
95            if (clicked && event_arr[0] === first[0] && event_arr[1] === first[1])
96            {
97                // compute range between the two clicked dots
98                var range = Math.abs(parseFloat(event_arr[2]) - parseFloat(first[2]));
99                var clicked_dots = [];
100                // checks highway direction to determine if postmiles are ascending or descending
101                if (parseFloat(event_arr[2]) < parseFloat(first[2]))
102                { 
103                    asc = false;
104                }
105                else 
106                {
107                    asc = true;
108                }
109                // searches map for all dots between the two clicked dots and adds it to list "clicked_dots"
110                map.data.forEach(function (marker) 
111                {
112                    var marker_arr = marker.getId().split(/[ ,]+/);
113                    // if the dot in the map matches the highway and direction of the clicked dot
114                    if (marker_arr[0] === event_arr[0] && marker_arr[1] === event_arr[1])
115                    {
116                        // compute distance based on direction of highway
117                        if (asc)
118                        {
119                            var dist = parseFloat(marker_arr[2]) - parseFloat(first[2]);
120                        }
121                        else 
122                        {
123                            var dist = parseFloat(parseFloat(first[2]) - (marker_arr[2]));
124                        }
125                        // if dist within range of the two clicked dots, add to list "clicked_dots"
126                        if (dist <= range && dist >= 0)
127                        {
128                            clicked_dots.push(marker);
129                        }
130                    }
131                });
132                // extracts ids for all the white dots
133                var white_arr_ids = white_arr.map(function (marker) {
134                    return marker.id;
135                });
136                // extracts ids for all the clicked dots
137                var clicked_dots_ids = clicked_dots.map(function (marker) {
138                    return marker.getId();
139                });
140                // computes difference between the white dots and clicked dots to find the unused dots
141                var unused_ids = white_arr_ids.diff(clicked_dots_ids);
142                //console.log("Unused: ",unused_ids, "White: ",white_arr_ids);
143                // change the dot color of all the unused white dots back to their previous color
144                unused_ids.forEach(function (id){
145                    var marker = white_arr.filter(function (dot) {
146                        return dot.id === id;
147                    })[0];
148                    var feature = map.data.getFeatureById(marker.id); 
149                    feature.setProperty("color", marker.color);
150                });
151                white_arr = [];
152                // change the color of all the dots between the two clicked dots to the user picked color
153                if (chosenColor)
154                {
155                    clicked_dots.forEach(function (dot) {
156                        dot.setProperty("color", chosenColor);
157                    });
158                }
159
160                // Create a printable string for the time (HH:MM:SS)
161                var printableTime = currentTime.toLocaleTimeString('it-IT');
162
163                // Assemble the line to be written to the events file
164                var lineOut = "101   " + printableTime + "\t" + first[0] + "\t" + 
165                first[1] + "\t" + first[2] + "\t" + range.toFixed(3) + "\t" 
166                + getColorAbbr(chosenColor);
167
168                // Using Ajax POST to send the data
169                var xhr = new XMLHttpRequest();
170                xhr.open("POST", "../../cgi-bin/saveTrafficEvent.py", true);
171                xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
172                // send the collected data
173                xhr.send("msg="+lineOut + "\n")
174
175                // Increment the time by one-half minute (30 seconds)
176                var millisecs = currentTime.getTime();
177                millisecs += 30000; 
178                currentTime = new Date(millisecs);
179                // Refresh the time display
180                updateTime(currentTime.toLocaleTimeString('it-IT'));
181
182            }
183            // if user has not yet clicked on a dot
184            else if (!clicked)
185            {
186                first = event_arr;
187
188                // turns relevant dots white
189                selectedID = event.feature.getId();  // get the id of the clicked dot
190                curr = postmileList.indexOf(selectedID); // find the id in the postmile list
191                var firstBlank = postmileList[curr].indexOf(" ", 0);  // find direction substring
192                var secondBlank = firstBlank+2; 
193                var direction = postmileList[curr].charAt(firstBlank+1); // extract direction
194                target = event.feature.getId().substring(0,secondBlank); // extract target string
195                //console.log("making white dots from " + selectedID + " in direction "+direction)
196                // Decide whether to increment or decrement based on direction of hwy;
197                //  N and E increment,  S and W decrement
198                if (direction == 'N' || direction == 'E')
199                {
200                    // Process all dots with same hwy and direction
201                    while  (postmileList[curr].startsWith(target) && curr < postmileList.length)
202                    {
203                        // turn the dot white
204                        currFeat = map.data.getFeatureById(postmileList[curr]);
205                        white_arr.push({id : currFeat.getId(), color : currFeat.getProperty("color")});         
206                        currFeat.setProperty("color", "white"); 
207                        curr++;
208               
209                    } 
210                }
211                else
212                {
213                    // Process all dots with same hwy and direction
214                    while  (postmileList[curr].startsWith(target) && curr > 0)
215                    {
216                        currFeat = map.data.getFeatureById(postmileList[curr]);
217                        white_arr.push({id : currFeat.getId(), color : currFeat.getProperty("color")});
218                        // turn the dot white
219                        currFeat.setProperty("color", "white"); 
220                        curr--;
221                    }                 
222                }
223            }
224            // if the user's second click is made on a dot not on the first dot's highway
225            else 
226            {
227                console.log('wrong choice');
228                clicked = !clicked;
229            }
230            clicked = !clicked;
231        });
232    }
233    // callback when load GeoJson completes
234    // save each feature's Point as the original coordinates for later reference
235    function saveCoords(features)
236    {
237        // Iterate over all the features in the map
238        features.forEach(function(feature)
239        {
240            var pt = feature.getGeometry().get();
241            vds_coords[feature.getId()] = pt; // save the Point in a dictionary
242            postmileList.push(feature.getId()); // save the postmile in a list
243        });
244        // update the dot colors from the dynamic json data
245        updateVDSlayer();
246        // go adjust the marker coordinates so dots don't overlap
247        adjustCoords(calcDistanceFactor());
248    } 
249
250    // magic formula controls distance between dots proportionate to zoom factor
251    function calcDistanceFactor()
252    {
253        // 15 is maximum zoom, the point at which no adjustment is needed
254        return (.0005 * (15 - map.getZoom()));
255    }
256
257    // Adjust the coordinates of dots so they appear side-by-side
258    // The perpendicular vector for each dot has been provided,
259    // so we just need to multiply by a scaling factor (adjAmount)
260    // @param adjAmount amount by which to adjust coordinate
261    function adjustCoords(adjAmount)
262    {
263        // Adjust the NB points a slight amount
264        map.data.forEach(function(feature)
265        {
266            // get the name of the current feature
267            var name = feature.getId();
268            // lookup the original coordinates for this feature
269            var coords = vds_coords[name];
270
271            //retrieve the perpendicular vector (precomputed)
272            var perpx = feature.getProperty("perpx")
273            var perpy = feature.getProperty("perpy")
274                // Make adjustment and save it
275            var myLat = coords.lat() + perpy * adjAmount
276            var myLong = coords.lng() + perpx * adjAmount
277            feature.setGeometry(
278            {
279                lat: myLat,
280                lng: myLong
281            });
282        });
283    }
284
285    // update the color (as needed) for a given marker
286    function updateMarker(marker)
287    {
288        target = marker.id;
289        newColor = marker.properties.color;
290        // see if new color is different than current color
291        currentFeature = map.data.getFeatureById(target);
292        if (currentFeature) {
293            currentColor = currentFeature.getProperty("color");
294            // if a new color is desired then assign it to the feature's color property
295            if (currentColor != newColor)
296            {
297                currentFeature.setProperty("color", newColor);
298                // set zIndex for slowed traffic to a higher value so they overlap
299                currentFeature.setProperty("zIndex", colorZvalues[newColor]);
300            }
301        }
302    }
303
304    // Load the highways dynamic json file and update the map
305    function updateVDSlayer()
306    {
307        eventIndex = -1;
308        var parsed_JSON;
309        loadJSON(kVDSstatusFile, function(response)
310        {
311            // Parse JSON string into object
312            parsed_JSON = JSON.parse(response);
313            // Process each new marker - lookup in current map
314            parsed_JSON.features.forEach(updateMarker);
315        });
316    }
317
318    function getColorName(str){
319        if (str === "R\r" || str === "R")
320            return "red";
321        else if (str === "Y\r" || str === "Y")
322            return "yellow";
323        else if (str === "G\r" || str === "G")
324            return "lime";
325    }
326
327    // gets the starting letter of a given color
328    function getColorAbbr(str){
329        if (str === "red")
330            return "R";
331        else if (str === "yellow")
332            return "Y";
333        else if (str === "lime")
334            return "G";
335    }
336
337function initColorButtons()
338{
339    // add the respective color buttons onto the map
340    var redColor = document.getElementById('redButton');
341    var greenColor = document.getElementById('greenButton');
342    var yellowColor = document.getElementById('yellowButton');
343    var time = document.getElementById('timebox');
344    map.controls[google.maps.ControlPosition.BOTTOM_CENTER].push(time);
345    map.controls[google.maps.ControlPosition.BOTTOM_CENTER].push(redColor);
346    map.controls[google.maps.ControlPosition.BOTTOM_CENTER].push(greenColor);
347    map.controls[google.maps.ControlPosition.BOTTOM_CENTER].push(yellowColor);
348    // store the user picked color in "chosenColor"
349   
350    redColor.addEventListener('click', function() {
351        chosenColor = "red";
352        redColor.style.background = "red";
353        greenColor.style.background = "gray";
354        yellowColor.style.background = "gray";
355       
356    });
357    greenColor.addEventListener('click', function() {
358        chosenColor = "lime";
359        greenColor.style.background = "green";
360        redColor.style.background = "gray";
361        yellowColor.style.background = "gray";       
362    });
363    yellowColor.addEventListener('click', function(){
364        chosenColor = "yellow";
365        yellowColor.style.background = "yellow";
366        redColor.style.background = "gray";
367        greenColor.style.background = "gray";     
368    });
369        // set default button on
370    yellowColor.style.background = "yellow";   
371}
Note: See TracBrowser for help on using the repository browser.