source: tmcsimulator/trunk/src/cptms/stms_map_v54a.html @ 284

Revision 284, 15.6 KB checked in by jdalbey, 7 years ago (diff)

Create new folder fo CPTMS including html, bash, and python scripts, and data files

Line 
1<!DOCTYPE html>
2<html>
3  <head>
4  <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
5    <title>CPTMS Map v0.5.5</title> 
6    <style>
7      /* Set the size of the div element that contains the map */
8      #mapdiv {
9        height: 100%;
10        width: 100%; 
11       }
12        /* Makes the page fill the window. */
13      html, body {
14        height: 100%;
15        margin: 0;
16        padding: 0;
17      }
18      #search-input {
19        background-color: #17263c;  /* #CD853F;  /*#E6E6FA; /* lavender */
20        color: #E6E6FA;  /* #FFEFD5; */
21        font-family: Roboto;
22        font-size: 18px;
23        font-weight: 400;
24        margin-left: 12px;
25        padding: 0 11px 0 13px;
26        text-overflow: ellipsis;
27        border-color: #746855;   /* #4d90fe; */
28        width: 400px;
29      }
30
31      #search-input:focus {
32        border-color: #E6E6FA; 
33      }
34
35      #ctrButton {
36        font-size: 40px;
37        margin-right: 9px;
38        background-color: #fff;
39        color: #47476b;
40        cursor: pointer;
41      }
42
43    </style>
44  </head>
45  <body>
46    <!-- Version 5.5 renames title to CPTMS
47         Version 5.4 adds Search box and Center button
48         Version 5.3 fixed dot color update defect, increased refresh rate to 10 sec.
49         Version 5.2 places red dots overlapping yellow dots.
50         Version 5.1 removes the map and street view buttons and the H3 tag.
51         Version 5 uses precomputed perpendicular vector in dot adjustment function
52         Version 4 Adjust the spacing between dots when the map is zoomed.
53         Version 3 does loadGeoJson only once, and subsequently does an ajax load
54         of the highways file, and selectively updates only those placePins whose
55         color has changed.
56         @author jdalbey  2019.2.17
57    -->
58    <!-- The text area input for the Search Box -->
59    <input id="search-input" class="controls" type="text" placeholder="Search Box">
60    <!--The div element where the map appears -->
61    <div id="mapdiv"></div>
62    <!--The div element where the center button appears -->
63    <div id="ctrButton">&#x2295;</div>
64    <script>
65    // a global variable for the google map
66    var map; 
67    // a global variable to hold locations of marked search places
68    var placePins = [];
69    // Constant name of json data file
70    var kMapPointsFile = "highways.json";
71    // Constant for map center location: The John Wayne Airport
72    //var centerPoint = {lat: 33.687228, lng: -117.872148};
73    // Constant for map center location in District 12
74    var centerPoint = {lat: 33.693385, lng: -117.798937};
75    // Initial map zoom
76    var initZoom = 11;
77    // Dot colors used in traffic model to indicate free-flowing, slowed, and stopped traffic
78    // and their associated zvalues so slower traffic dots are more visible.
79    var colorZvalues = {"lime":10,"yellow":20,"red":30};
80
81    // Build a solid colored icon to use instead of the classic pin
82    // Use a diamond on N and E directions, circle on S and W directions
83    function dotSymbol(color,postmileID) //,direction)
84    {
85        var circle = google.maps.SymbolPath.CIRCLE;
86        var diamond = 'M -1,0 0,-1 1,0 0,1 z';
87        var myShape = circle;
88        // See if postmile name contains N or W letters
89        //if ((postmileID.indexOf('N') != -1) || (postmileID.indexOf('W') != -1))
90        //{
91        //   myShape = diamond
92        //}
93        return {
94            path: myShape,
95            scale: 5,
96            strokeColor: "black", // the border color
97            strokeWeight: 1,      // the border thickness
98            fillColor: color,
99            fillOpacity: 1.0
100        };
101    }
102
103    // Load the map data from a json file and style all the points
104    function loadMapData()
105    {
106        // Load the map data
107        map.data.loadGeoJson(kMapPointsFile);
108        // Style the map data by applying the desired properties to each feature (marker)
109        map.data.setStyle(function(feature) 
110        {
111            // Get the desired color value
112            var ptColor = feature.getProperty("color");
113            // Get the postmile id
114            var name = feature.getId();
115            var street = feature.getProperty("street");
116            // Build the marker
117            var iconSymbol = dotSymbol(ptColor,name);
118            // return the StyleOptions
119            return {
120                    icon: iconSymbol,
121                    title: name + " @" + street,  // set rollover text
122                    // set zIndex for slowed traffic to a higher value so they overlap
123                    zIndex: colorZvalues[ptColor]
124                   };
125        });
126    }
127
128    // magic formula controls distance between dots proportionate to zoom factor
129    function calcDistanceFactor()
130    {
131        // 15 is maximum zoom, the point at which no adjusment is needed
132        return (.0005*(15-map.getZoom())); 
133    }
134
135    // Adjust the coordinates of dots so they appear side-by-side
136    // The perpendicular vector for each dot has been provided,
137    // so we just need to multiply by a scaling factor (adjAmount)
138    // @param adjAmount amount by which to adjust coordinate
139    function adjustCoords(adjAmount)
140    {
141        //console.log("adjusting coordinates");
142        // Adjust the NB points a slight amount
143        map.data.forEach(function(feature)
144        {
145            // get the name of the current feature
146            var name = feature.getId();
147            // lookup the name in the list of placePins
148            var marker = parsed_JSON.features.find(function(element)
149                {
150                    return element.id == name;
151                });
152            // extract the original coordinates
153            origLat = marker.geometry.coordinates[1];
154            origLong = marker.geometry.coordinates[0];
155
156            //retrieve the perpendicular vector (precomputed)
157            var perpx = feature.getProperty("perpx")
158            var perpy = feature.getProperty("perpy")
159            // Make adjustment and save it
160            var myLat = origLat + perpy * adjAmount
161            var myLong = origLong + perpx * adjAmount
162            feature.setGeometry({lat:myLat, lng:myLong});
163        });
164    }
165
166     // update the color (as needed) for a given marker
167     function updateMarker(marker)
168     {
169        target = marker.id;
170        newColor = marker.properties.color;
171        // see if new color is different than current color
172        currentFeature = map.data.getFeatureById(target);
173        currentColor = currentFeature.getProperty("color");
174        //console.log("current color:",currentColor," newColor: ",newColor);
175        // if a new color is desired then assign it to the feature's color property
176        if (currentColor != newColor)
177        {
178            currentFeature.setProperty("color",newColor);
179            // set zIndex for slowed traffic to a higher value so they overlap
180            currentFeature.setProperty("zIndex", colorZvalues[newColor]);
181            //console.log(target+" updated to "+newColor);
182        }
183     }
184
185    // Load the highways file via ajax
186    // Ref: https://codepen.io/KryptoniteDove/post/load-json-file-locally-using-pure-javascript
187     function loadJSON(callback) {   
188
189        var xobj = new XMLHttpRequest();
190            xobj.overrideMimeType("application/json");
191        xobj.open('GET', kMapPointsFile, true); 
192        xobj.onreadystatechange = function () {
193              if (xobj.readyState == 4 && xobj.status == "200") {
194                // Required use of an anonymous callback as .open will NOT return a value but simply returns undefined in asynchronous mode
195                callback(xobj.responseText);
196              }
197        };
198        xobj.send(null); 
199     }
200
201     var parsed_JSON;
202     // Load the highways json file and update the map
203     function updateMap()
204     {
205        loadJSON(function(response)
206        {
207            // Parse JSON string into object
208            parsed_JSON = JSON.parse(response);
209            // Process each new marker - lookup in current map
210            parsed_JSON.features.forEach(updateMarker);
211        });
212     }
213
214    // Initialize the center button (to re-center the map)
215    function initCenter()
216    {
217        var centerBtnDiv = document.getElementById('ctrButton');
218        map.controls[google.maps.ControlPosition.RIGHT_CENTER].push(centerBtnDiv)
219        centerBtnDiv.title = 'Click to recenter the map';
220
221        // Setup the click event listeners: reset center location and zoom factor
222        centerBtnDiv.addEventListener('click', function() {
223          map.setCenter(centerPoint);
224          map.setZoom(initZoom);
225          clearPlacePins();
226        });
227    }
228
229    // Initialize the search box and listener
230    function initSearch()
231    {
232        // Create the search box and link it to the UI element.
233        var input = document.getElementById('search-input');
234        var searchBox = new google.maps.places.SearchBox(input);
235        map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);
236
237        // Bias the SearchBox results towards current map's viewport.
238        map.addListener('bounds_changed', function() {
239          searchBox.setBounds(map.getBounds());
240        });
241
242        // Listen for the event fired when the user selects a prediction and retrieve
243        // more details for that place.
244        searchBox.addListener('places_changed', function() {
245          var places = searchBox.getPlaces();
246
247          if (places.length == 0) {
248            return;
249          }
250
251          clearPlacePins();
252
253          // Create a bounding region to include the search result places
254          var bounds = new google.maps.LatLngBounds();
255          // For each place, get the icon, name and location.
256          // There may be multiple search results
257          places.forEach(function(place) {
258            if (!place.geometry) {
259              console.log("Returned place contains no geometry");
260              return;
261            }
262
263            // Create a marker for each place.
264            placeMarker = new google.maps.Marker({
265              map: map,
266              title: place.name,
267              position: place.geometry.location
268            })
269
270            // Click on the marker to remove it from the display
271            placeMarker.addListener('click', function() {
272              placeMarker.setMap(null);
273            });
274
275            // Add this marker to the collection of current markers
276            placePins.push(placeMarker);
277
278            // Create a bounding region to include this place
279            if (place.geometry.viewport) {
280              // Only geocodes have viewport.
281              bounds.union(place.geometry.viewport);
282            } else {
283              bounds.extend(place.geometry.location);
284            }
285          });
286          // This will pan and zoom to the area around the marker
287          //map.fitBounds(bounds);
288          // This will center the map on the new marker(s) but not zoom
289          map.setCenter(bounds.getCenter());
290        });
291    }
292
293    // Remove any place pins from a previous search
294    function clearPlacePins()
295    {
296          placePins.forEach(function(marker) {
297            marker.setMap(null);
298          });
299          placePins = [];
300    }
301
302    // Initialize the map and load the points
303    function initMap() 
304    {
305        // Declare the map and where it belongs on the page
306        map = new google.maps.Map( document.getElementById('mapdiv'), 
307        {
308            zoom: initZoom, 
309            center: centerPoint,
310            styles: night_mode,
311            mapTypeControl: false,
312            streetViewControl: false 
313        });
314        initSearch();
315        initCenter();
316        loadMapData();  // go load the map data
317        // After a brief pause to let map finish loading ...
318        quiktimer = setTimeout("updateMap()",3000);
319        // go adjust the marker coordinates so dots don't overlap
320        quiktimer = setTimeout("adjustCoords(calcDistanceFactor())",3500); 
321        // Start a timer to refresh the map every 10 seconds
322        var myTimer = setInterval(updateMap, 10000);
323        // Listen for zoom changes and move the placePins so as to keep a nice
324        // visual distance between them appropriate to the zoom factor
325        map.addListener('zoom_changed', function() {
326            // fetch how much the map is currently zoomed
327            currentZoom = map.getZoom(); 
328            //console.log("Zoom changed to ",currentZoom);
329            // only bother adjusting within this range
330            if ((currentZoom <16) && (currentZoom>10))
331            {
332                // magic formula controls distance between dots
333                factor = (.0005*(15-currentZoom)); 
334                adjustCoords(calcDistanceFactor());
335            }
336        });
337
338    }
339
340    // Styles array for Night Mode map
341    // Ref: https://developers.google.com/maps/documentation/javascript/styling
342    var night_mode = [
343                {elementType: 'geometry', stylers: [{color: '#242f3e'}]},
344                {elementType: 'labels.text.stroke', stylers: [{color: '#242f3e'}]},
345                {elementType: 'labels.text.fill', stylers: [{color: '#746855'}]},
346                {
347                  featureType: 'administrative.locality',
348                  elementType: 'labels.text.fill',
349                  stylers: [{color: '#d59563'}]
350                },
351                {
352                  featureType: 'poi',
353                  elementType: 'labels.text.fill',
354                  stylers: [{color: '#d59563'}]
355                },
356                {
357                  featureType: 'poi.park',
358                  elementType: 'geometry',
359                  stylers: [{color: '#263c3f'}]
360                },
361                {
362                  featureType: 'poi.park',
363                  elementType: 'labels.text.fill',
364                  stylers: [{color: '#6b9a76'}]
365                },
366                {
367                  featureType: 'road',
368                  elementType: 'geometry',
369                  stylers: [{color: '#38414e'}]
370                },
371                {
372                  featureType: 'road',
373                  elementType: 'geometry.stroke',
374                  stylers: [{color: '#212a37'}]
375                },
376                {
377                  featureType: 'road',
378                  elementType: 'labels.text.fill',
379                  stylers: [{color: '#9ca5b3'}]
380                },
381                {
382                  featureType: 'road.highway',
383                  elementType: 'geometry',
384                  stylers: [{color: '#746855'}]
385                },
386                {
387                  featureType: 'road.highway',
388                  elementType: 'geometry.stroke',
389                  stylers: [{color: '#1f2835'}]
390                },
391                {
392                  featureType: 'road.highway',
393                  elementType: 'labels.text.fill',
394                  stylers: [{color: '#f3d19c'}]
395                },
396                {
397                  featureType: 'transit',
398                  elementType: 'geometry',
399                  stylers: [{color: '#2f3948'}]
400                },
401                {
402                  featureType: 'transit.station',
403                  elementType: 'labels.text.fill',
404                  stylers: [{color: '#d59563'}]
405                },
406                {
407                  featureType: 'water',
408                  elementType: 'geometry',
409                  stylers: [{color: '#17263c'}]
410                },
411                {
412                  featureType: 'water',
413                  elementType: 'labels.text.fill',
414                  stylers: [{color: '#515c6d'}]
415                },
416                {
417                  featureType: 'water',
418                  elementType: 'labels.text.stroke',
419                  stylers: [{color: '#17263c'}]
420                }
421              ]
422
423    // Using John's API Key
424    </script>
425    <script async defer
426    src="https://maps.googleapis.com/maps/api/js?key=AIzaSyD6iTyN0DjP-9OVkAgicyp4tkC10naE_B8&libraries=places&callback=initMap">
427    </script>
428  </body>
429</html>
Note: See TracBrowser for help on using the repository browser.