RideLogicAVLS.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. //
  2. // Copyright (c) 2021 Clementine Computing LLC.
  3. //
  4. // This file is part of RideLogic.
  5. //
  6. // RideLogic is free software: you can redistribute it and/or modify
  7. // it under the terms of the GNU Affero General Public License as published by
  8. // the Free Software Foundation, either version 3 of the License, or
  9. // (at your option) any later version.
  10. //
  11. // RideLogic is distributed in the hope that it will be useful,
  12. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. // GNU Affero General Public License for more details.
  15. //
  16. // You should have received a copy of the GNU Affero General Public License
  17. // along with RideLogic. If not, see <https://www.gnu.org/licenses/>.
  18. //
  19. var Feature = ol.Feature;
  20. var Map = ol.Map;
  21. var Overlay = ol.Overlay;
  22. var Point = ol.geom.Point;
  23. var TileJSON = ol.source.TileJSON;
  24. var VectorSource = ol.source.Vector;
  25. var View = ol.View;
  26. var Icon = ol.style.Icon;
  27. var Style = ol.style.Style;
  28. var TileLayer = ol.layer.Tile;
  29. var VectorLayer = ol.layer.Vector;
  30. var OSM = ol.source.OSM;
  31. var fromLonLat = ol.proj.fromLonLat;
  32. var Control = ol.control.Control;
  33. var LineString = ol.geom.LineString;
  34. //var defaultLonLat = [ -76.5019, 42.4440];
  35. //var defaultLonLat = [ -76.1805, 42.6012 ];
  36. var defaultLonLat = [ -76.2705, 42.5512 ];
  37. var defaultWebMerc = fromLonLat(defaultLonLat);
  38. function _rnd(a,b) {
  39. a = ((typeof a == "undefined") ? 1.0 : a);
  40. if (typeof b === "undefined") {
  41. return Math.random()*a;
  42. }
  43. return Math.random()*(b-a) + a;
  44. }
  45. function example_change_pos() {
  46. let w = 1/16.0;
  47. let lonlat = [
  48. defaultLonLat[0] + _rnd(-w,w),
  49. defaultLonLat[1] + _rnd(-w,w)];
  50. let merc = fromLonLat(lonlat);
  51. _icon[0].feature.getGeometry().setCoordinates(merc);
  52. busLayer.getSource().changed();
  53. }
  54. function example_change_opacity(p) {
  55. p = ((typeof p === "undefined") ? 0.0 : p);
  56. _icon[0].style.getImage().setOpacity(p);
  57. busLayer.getSource().changed();
  58. }
  59. // icon can't change image src dynamically,
  60. // so have to replace.
  61. // See https://stackoverflow.com/questions/57341190/how-can-i-change-icon-source-dynamically
  62. //
  63. function example_change_image() {
  64. let _nuimg = new Icon({
  65. anchor: [0.5, 46],
  66. anchorXUnits: 'fraction',
  67. anchorYUnits: 'pixels',
  68. src: 'data/bus_gw_90.png',
  69. });
  70. _icon[0].style.setImage(_nuimg);
  71. busLayer.getSource().changed();
  72. }
  73. //---
  74. //var vectorSource = new VectorSource({ features: [iconFeature], });
  75. var vectorSource = new VectorSource();
  76. var _icon = [];
  77. for (let ii=0; ii<2; ii++) {
  78. let w = 1/32.0;
  79. let lonlat = [
  80. defaultLonLat[0] + _rnd(-w,w),
  81. defaultLonLat[1] + _rnd(-w,w)];
  82. let merc = fromLonLat(lonlat);
  83. let iconFeature = new Feature({
  84. geometry: new Point([merc[0], merc[1]]),
  85. name: 'bus'
  86. });
  87. let iconStyle = new Style({
  88. image: new Icon({
  89. anchor: [0.5, 46],
  90. anchorXUnits: 'fraction',
  91. anchorYUnits: 'pixels',
  92. src: 'data/icon.png',
  93. }),
  94. });
  95. iconStyle.getImage().setOpacity(0.0);
  96. _icon.push({ "style": iconStyle, "feature": iconFeature });
  97. iconFeature.setStyle(iconStyle);
  98. vectorSource.addFeature(iconFeature);
  99. }
  100. //---
  101. const busLayer = new VectorLayer({
  102. source: vectorSource,
  103. zIndex: 2
  104. });
  105. //--
  106. const rasterLayer = new TileLayer({
  107. //source: ol.source.OSM()
  108. source: new ol.source.OSM(),
  109. });
  110. //--
  111. //-------------------
  112. //-------------------
  113. //-------------------
  114. const g_map = new Map({
  115. //layers: [rasterLayer, vectorLayer, routeLayer],
  116. layers: [rasterLayer, busLayer ],
  117. target: document.getElementById('map'),
  118. controls:[],
  119. view: new View({
  120. center: defaultWebMerc,
  121. zoom: 11,
  122. }),
  123. });
  124. class CustomToggle extends Control {
  125. constructor(opt) {
  126. opt = opt || {};
  127. let ele = document.createElement("button");
  128. ele.id = 'ui_route_toggle';
  129. ele.innerHTML = "Route";
  130. // class="btn" id="sidebarToggle" style='display:float;'
  131. ele.classList.add("custom-button");
  132. super({
  133. element: ele,
  134. target: opt.target
  135. });
  136. ele.addEventListener('click', this.fire.bind(this), false);
  137. }
  138. fire() {
  139. document.body.classList.toggle('sb-sidenav-toggled');
  140. localStorage.setItem('sb|sidebar-toggle', document.body.classList.contains('sb-sidenav-toggled'));
  141. // hacky, but functional
  142. //
  143. setTimeout(_resize, 250);
  144. }
  145. }
  146. g_map.addControl(new CustomToggle({ className: 'custom-zoom' }));
  147. //-------------------
  148. //-------------------
  149. //-------------------
  150. g_map.addControl(new ol.control.Zoom({
  151. className: 'custom-zoom'
  152. }));
  153. const element = document.getElementById('popup');
  154. const popup = new Overlay({
  155. element: element,
  156. positioning: 'bottom-center',
  157. stopEvent: false,
  158. });
  159. g_map.addOverlay(popup);
  160. // display popup on click
  161. //
  162. g_map.on('click', function (evt) {
  163. // not working (wip)
  164. //
  165. /*
  166. const feature = map.forEachFeatureAtPixel(evt.pixel, function (feature) {
  167. return feature;
  168. });
  169. */
  170. });
  171. // change mouse cursor when over marker
  172. g_map.on('pointermove', function (e) {
  173. const pixel = g_map.getEventPixel(e.originalEvent);
  174. const hit = g_map.hasFeatureAtPixel(pixel);
  175. g_map.getTarget().style.cursor = hit ? 'pointer' : '';
  176. });
  177. // Close the popup when the map is moved
  178. g_map.on('movestart', function () {
  179. //$(element).popover('dispose');
  180. });
  181. g_map.on("postrender", function(event) {
  182. //console.log("bang...");
  183. });
  184. //----
  185. //
  186. setInterval( function() {
  187. //console.log("bang");
  188. //map.render();
  189. }, 1000);
  190. //-----
  191. //
  192. function update_buses(json_gtfs) {
  193. let bearingIdxLookup = [ "0", "45", "90", "135", "180", "225", "270", "315" ];
  194. for (let ii=0; ii<json_gtfs.entityList.length; ii++) {
  195. let id = json_gtfs.entityList[ii].id;
  196. let p = json_gtfs.entityList[ii].vehicle.position;
  197. let lat = p.latitude;
  198. let lon = p.longitude;
  199. let bearing = p.bearing;
  200. let lonlat = [ lon , lat ];
  201. let merc = fromLonLat(lonlat);
  202. if (typeof _icon[ii] === "undefined") { continue; }
  203. _icon[ii].feature.getGeometry().setCoordinates(merc);
  204. bearing = (bearing % 360 );
  205. if (bearing < 0) { bearing += 360.0; }
  206. let iheading = Math.floor( (parseInt( bearing ) + 23) / 45 );
  207. if (iheading > 7) { iheading = 0; }
  208. let _nuimg = new Icon({
  209. anchor: [0.5, 46],
  210. anchorXUnits: 'fraction',
  211. anchorYUnits: 'pixels',
  212. src: 'data/bus_gw_' + bearingIdxLookup[iheading] + '.png',
  213. });
  214. _icon[ii].style.setImage(_nuimg);
  215. _icon[ii].style.getImage().setOpacity(1.0);
  216. //console.log(">>", id, lat, lon, bearing);
  217. }
  218. busLayer.getSource().changed();
  219. }
  220. function fetch_gtfs_vehicle_position() {
  221. let xhr = new XMLHttpRequest();
  222. xhr.onload = function() {
  223. let ab = xhr.response;
  224. let json_pb = pb2json.pb2json(ab);
  225. //console.log(">>>", json_pb);
  226. update_buses(json_pb)
  227. };
  228. xhr.responseType = "arraybuffer";
  229. xhr.open("GET", "VehiclePosition.pb");
  230. xhr.send();
  231. }
  232. //--
  233. function update_route_layer(geojson) {
  234. if (!geojson) { return; }
  235. let route_points = geojson.features[0].geometry.coordinates;
  236. let merc_points = [];
  237. for (let ii=0; ii<route_points.length; ii++) {
  238. merc_points.push(ol.proj.transform(route_points[ii], 'EPSG:4326', 'EPSG:3857'));
  239. }
  240. let routeLine = new ol.Feature({
  241. geometry: new ol.geom.LineString(merc_points)
  242. });
  243. let routeLineVector = new ol.source.Vector({});
  244. routeLineVector.addFeature(routeLine);
  245. let routeLayer = new VectorLayer({
  246. source: routeLineVector,
  247. style: new ol.style.Style({
  248. fill: new ol.style.Fill({ color: 'rgb(0,0,255,0.25)', weight: 4, opacity: 0.5 }),
  249. stroke: new ol.style.Stroke({ color: 'rgb(0,0,255,0.25)', width: 6, opacity: 0.5 })
  250. }),
  251. zIndex: 0
  252. });
  253. g_map.addLayer(routeLayer);
  254. }
  255. function update_stop_layer(geojson) {
  256. if (!geojson) { return; }
  257. let stop_points = geojson.features[0].properties.route_stops;
  258. if (!stop_points) { return; }
  259. let merc_points = [];
  260. for (let ii=0; ii<stop_points.length; ii++) {
  261. let pnt = stop_points[ii].stop.geometry.coordinates;
  262. merc_points.push(ol.proj.transform(pnt, 'EPSG:4326', 'EPSG:3857'));
  263. }
  264. let stopVectorSource = new VectorSource();
  265. for (let ii=0; ii<merc_points.length; ii++) {
  266. let _stop = stop_points[ii].stop;
  267. let _stop_name = _stop.stop_name;
  268. let iconFeature = new Feature({
  269. geometry: new Point([merc_points[ii][0], merc_points[ii][1]]),
  270. name: _stop_name
  271. });
  272. let iconStyle = new Style({
  273. image: new Icon({
  274. anchor: [0.5, 46],
  275. anchorXUnits: 'fraction',
  276. anchorYUnits: 'pixels',
  277. src: 'data/star-3.png',
  278. }),
  279. });
  280. iconStyle.getImage().setOpacity(0.8);
  281. iconFeature.setStyle(iconStyle);
  282. stopVectorSource.addFeature(iconFeature);
  283. }
  284. let stopLayer = new VectorLayer({
  285. source: stopVectorSource,
  286. zIndex: 0
  287. });
  288. g_map.addLayer(stopLayer);
  289. }
  290. function fetch_geojson(url) {
  291. let xhr = new XMLHttpRequest();
  292. xhr.onload = function() {
  293. let geojson = xhr.response;
  294. update_route_layer(geojson);
  295. update_stop_layer(geojson);
  296. };
  297. xhr.responseType = "json";
  298. xhr.open("GET", url, true);
  299. xhr.send();
  300. }
  301. //---
  302. function _resize() {
  303. let h = $(window).height();
  304. let m = document.getElementById("map");
  305. m.style.height = (h-10) + "px";
  306. g_map.updateSize();
  307. }
  308. function init() {
  309. setInterval(fetch_gtfs_vehicle_position, 1000);
  310. setTimeout( function() { fetch_geojson("geojson/test_route.geojson"); }, 100);
  311. $(window).on('resize', _resize);
  312. }
  313. window.addEventListener('DOMContentLoaded', event => {
  314. // Toggle the side navigation
  315. const sidebarToggle = document.body.querySelector('#sidebarToggle');
  316. if (sidebarToggle) {
  317. // Uncomment Below to persist sidebar toggle between refreshes
  318. // if (localStorage.getItem('sb|sidebar-toggle') === 'true') {
  319. // document.body.classList.toggle('sb-sidenav-toggled');
  320. // }
  321. sidebarToggle.addEventListener('click', event => {
  322. event.preventDefault();
  323. document.body.classList.toggle('sb-sidenav-toggled');
  324. localStorage.setItem('sb|sidebar-toggle', document.body.classList.contains('sb-sidenav-toggled'));
  325. // hacky, but functional
  326. //
  327. setTimeout(_resize, 250);
  328. });
  329. }
  330. //init();
  331. //_resize();
  332. });
  333. $(document).ready(function() {
  334. init();
  335. _resize();
  336. });