Improved www.ttss.krakow.pl
Jacek Kowalski
2017-04-23 8b6250cb5db8d1bb17902d45f08a2e929e8e0afd
commit | author | age
57b8d3 1 //var ttss_base = 'http://www.ttss.krakow.pl/internetservice';
8b6250 2 var ttss_base = 'proxy.php';
f36d1f 3 var ttss_refresh = 10000; // 10 seconds
57b8d3 4
JK 5 var vehicles_xhr = null;
6 var vehicles_timer = null;
7 var vehicles_last_update = 0;
8 var vehicles_source = null;
9 var vehicles_layer = null;
10
11 var stops_xhr = null;
12 var stops_source = null;
13 var stops_layer = null;
14 var stop_points_source = null;
15 var stop_points_layer = null;
16
07c714 17 var feature_id = null;
8b6250 18 var feature_xhr = null;
JK 19 var feature_timer = null;
07c714 20
57b8d3 21 var map = null;
JK 22 var popup_element = document.getElementById('popup');
23 var fail_element = document.getElementById('fail');
24
7ca6a1 25 var ignore_hashchange = false;
JK 26
57b8d3 27 function fail(msg) {
JK 28     console.log(msg);
29     
30     setText(fail_element, msg);
31     fail_element.style.top = '0.5em';
32 }
33
8b6250 34 function fail_popup(msg) {
JK 35     addElementWithText(popup_element, 'p', msg).className = 'error';
36 }
37
38 function fail_ajax_generic(data, fnc) {
57b8d3 39     // abort() is not a failure
JK 40     if(data.readyState == 0 && data.statusText == 'abort') return;
41     
42     if(data.status == 0) {
8b6250 43         fnc(lang.error_request_failed_connectivity, data);
57b8d3 44     } else if (data.statusText) {
8b6250 45         fnc(lang.error_request_failed_status.replace('$status', data.statusText), data);
57b8d3 46     } else {
8b6250 47         fnc(lang.error_request_failed, data);
57b8d3 48     }
8b6250 49 }
JK 50
51 function fail_ajax(data) {
52     fail_ajax_generic(data, fail);
53 }
54
55 function fail_ajax_popup(data) {
56     fail_ajax_generic(data, fail_popup);
57b8d3 57 }
JK 58
59 function getGeometry(object) {
07c714 60     return new ol.geom.Point(ol.proj.fromLonLat([object.longitude / 3600000.0, object.latitude / 3600000.0]));
57b8d3 61 }
JK 62
63 function updateVehicles() {
64     if(vehicles_timer) clearTimeout(vehicles_timer);
65     if(vehicles_xhr) vehicles_xhr.abort();
66     
67     vehicles_xhr = $.get(
68         ttss_base + '/geoserviceDispatcher/services/vehicleinfo/vehicles' 
69             + '?positionType=CORRECTED'
70             + '&colorType=ROUTE_BASED'
71             + '&lastUpdate=' + encodeURIComponent(vehicles_last_update)
72     ).done(function(data) {
73         vehicles_last_update = data.lastUpdate;
74         
75         for(var i = 0; i < data.vehicles.length; i++) {
76             var vehicle = data.vehicles[i];
77             
78             var vehicle_feature = vehicles_source.getFeatureById('v' + vehicle.id);
79             if(vehicle.isDeleted) {
80                 if(vehicle_feature) {
81                     vehicles_source.removeFeature(vehicle_feature);
07c714 82                     if(feature_id == vehicle_feature.getId()) {
JK 83                         featureClicked();
57b8d3 84                     }
JK 85                 }
86                 continue;
87             }
88             
89             var vehicle_name_space = vehicle.name.indexOf(' ');
90             vehicle.line = vehicle.name.substr(0, vehicle_name_space);
91             vehicle.direction = vehicle.name.substr(vehicle_name_space+1);
92             if(special_directions[vehicle.direction]) {
93                 vehicle.line = special_directions[vehicle.direction];
94             }
95             
96             vehicle.geometry = getGeometry(vehicle);
97             vehicle.vehicle_type = parseVehicle(vehicle.id);
98             
99             if(!vehicle_feature) {
100                 vehicle_feature = new ol.Feature(vehicle);
101                 vehicle_feature.setId('v' + vehicle.id);
102                 
103                 var color_type = 'black';
104                 if(vehicle.vehicle_type) {
105                     switch(vehicle.vehicle_type.low) {
106                         case 0:
107                             color_type = 'orange';
108                             break;
109                         case 1:
110                             color_type = 'blue';
111                             break;
112                         case 2:
113                             color_type = 'green';
114                             break;
115                     }
116                 }
117                 
118                 vehicle_feature.setStyle(new ol.style.Style({
7a2fc1 119                     image: new ol.style.Icon({
JK 120                         src: 'data:image/svg+xml;base64,' + btoa('<svg xmlns="http://www.w3.org/2000/svg" height="30" width="20"><polygon points="10,0 20,23 0,23" style="fill:#3399ff;stroke:'+color_type+';stroke-width:2" /></svg>'),
57b8d3 121                         rotation: Math.PI * parseFloat(vehicle.heading) / 180.0,
JK 122                     }),
123                     text: new ol.style.Text({
124                         font: 'bold 10px sans-serif',
125                         text: vehicle.line,
126                         fill: new ol.style.Fill({color: 'white'}),
127                     }),
128                 }));
129                 vehicles_source.addFeature(vehicle_feature);
130             } else {
131                 vehicle_feature.setProperties(vehicle);
132                 vehicle_feature.getStyle().getImage().setRotation(Math.PI * parseFloat(vehicle.heading) / 180.0);
133             }
134         }
135         
136         vehicles_timer = setTimeout(function() {
137             updateVehicles();
138         }, ttss_refresh);
139     }).fail(fail_ajax);
7ca6a1 140     
JK 141     return vehicles_xhr;
57b8d3 142 }
JK 143
144 function updateStopSource(stops, prefix, source) {
145     source.clear();
146     
147     for(var i = 0; i < stops.length; i++) {
148         var stop = stops[i];
e61357 149         
JK 150         if(stop.category == 'other') continue;
151         
57b8d3 152         stop.geometry = getGeometry(stop);
JK 153         var stop_feature = new ol.Feature(stop);
154         
155         stop_feature.setId(prefix + stop.id);
156         stop_feature.setStyle(new ol.style.Style({
157             image: new ol.style.Circle({
158                 fill: new ol.style.Fill({color: 'orange'}),
159                 stroke: new ol.style.Stroke({color: 'red', width: 1}),
160                 radius: 3,
161             }),
162         }));
163         
164         source.addFeature(stop_feature);
165     }
166 }
167
168 function updateStops() {
7ca6a1 169     return $.get(
57b8d3 170         ttss_base + '/geoserviceDispatcher/services/stopinfo/stops'
JK 171             + '?left=-648000000'
172             + '&bottom=-324000000'
173             + '&right=648000000'
174             + '&top=324000000'
175     ).done(function(data) {
176         updateStopSource(data.stops, 's', stops_source);
177     }).fail(fail_ajax);
178 }
179
180 function updateStopPoints() {
7ca6a1 181     return $.get(
57b8d3 182         ttss_base + '/geoserviceDispatcher/services/stopinfo/stopPoints'
JK 183             + '?left=-648000000'
184             + '&bottom=-324000000'
185             + '&right=648000000'
186             + '&top=324000000'
187     ).done(function(data) {
188         updateStopSource(data.stopPoints, 'p', stop_points_source);
189     }).fail(fail_ajax);
7ca6a1 190 }
JK 191
8b6250 192 function vehicleTable(tripId, table) {
JK 193     if(feature_xhr) feature_xhr.abort();
194     if(feature_timer) clearTimeout(feature_timer);
195     
196     feature_xhr = $.get(
197         ttss_base + '/services/tripInfo/tripPassages'
198             + '?tripId=' + encodeURIComponent(tripId)
199             + '&mode=departure'
200     ).done(function(data) {
201         if(!data.routeName || !data.directionText || data.old.length + data.actual.length == 0) {
202             return;
203         }
204         
205         deleteChildren(table);
206         
207         for(var i = 0, il = data.old.length; i < il; i++) {
208             var tr = document.createElement('tr');
209             addCellWithText(tr, data.old[i].actualTime || data.old[i].plannedTime);
210             addCellWithText(tr, data.old[i].stop_seq_num + '. ' + data.old[i].stop.name);
211             
212             tr.className = 'active';
213             table.appendChild(tr);
214         }
215         
216         for(var i = 0, il = data.actual.length; i < il; i++) {
217             var tr = document.createElement('tr');
218             addCellWithText(tr, data.actual[i].actualTime || data.actual[i].plannedTime);
219             addCellWithText(tr, data.actual[i].stop_seq_num + '. ' + data.actual[i].stop.name);
220             
221             if(data.actual[i].status == 'STOPPING') {
222                 tr.className = 'success';
223             }
224             table.appendChild(tr);
225         }
226         
227         feature_timer = setTimeout(function() { vehicleTable(tripId, table); }, ttss_refresh);
228     }).fail(fail_ajax_popup);
229 }
230
231 function stopTable(stopType, stopId, table) {
232     if(feature_xhr) feature_xhr.abort();
233     if(feature_timer) clearTimeout(feature_timer);
234     
235     feature_xhr = $.get(
236         ttss_base + '/services/passageInfo/stopPassages/' + stopType
237             + '?' + stopType + '=' + encodeURIComponent(stopId)
238             + '&mode=departure'
239     ).done(function(data) {
240         deleteChildren(table);
241         
242         for(var i = 0, il = data.old.length; i < il; i++) {
243             var tr = document.createElement('tr');
244             addCellWithText(tr, data.old[i].patternText);
245             var dir_cell = addCellWithText(tr, data.old[i].direction);
246             var vehicle = parseVehicle(data.old[i].vehicleId);
247             dir_cell.appendChild(displayVehicle(vehicle));
248             var status = parseStatus(data.old[i]);
249             addCellWithText(tr, status);
250             addCellWithText(tr, '');
251             
252             tr.className = 'active';
253             table.appendChild(tr);
254         }
255         
256         for(var i = 0, il = data.actual.length; i < il; i++) {
257             var tr = document.createElement('tr');
258             addCellWithText(tr, data.actual[i].patternText);
259             var dir_cell = addCellWithText(tr, data.actual[i].direction);
260             var vehicle = parseVehicle(data.actual[i].vehicleId);
261             dir_cell.appendChild(displayVehicle(vehicle));
262             var status = parseStatus(data.actual[i]);
263             var status_cell = addCellWithText(tr, status);
264             var delay = parseDelay(data.actual[i]);
265             var delay_cell = addCellWithText(tr, delay);
266             
267             if(status == lang.boarding_sign) {
268                 tr.className = 'success';
269                 status_cell.className = 'status-boarding';
270             } else if(parseInt(delay) > 9) {
271                 tr.className = 'danger';
272                 delay_cell.className = 'status-delayed';
273             } else if(parseInt(delay) > 3) {
274                 tr.className = 'warning';
275             }
276             
277             table.appendChild(tr);
278         }
279         
280         feature_timer = setTimeout(function() { stopTable(stopType, stopId, table); }, ttss_refresh);
281     }).fail(fail_ajax_popup);
282 }
283
7ca6a1 284 function featureClicked(feature) {
JK 285     if(!feature) {
07c714 286         feature_id = null;
JK 287         
288         $(popup_element).removeClass('show');
7ca6a1 289         
JK 290         ignore_hashchange = true;
291         window.location.hash = '';
292         
293         return;
294     }
295     
296     var coordinates = feature.getGeometry().getCoordinates();
297     
298     deleteChildren(popup_element);
299     
8b6250 300     var close = addParaWithText(popup_element, '×');
JK 301     close.className = 'close';
302     close.addEventListener('click', function() { featureClicked(); });
303     
07c714 304     var type;
JK 305     var name = feature.get('name');
306     var additional;
8b6250 307     var table = document.createElement('table');
JK 308     var thead = document.createElement('thead');
309     var tbody = document.createElement('tbody');
310     table.appendChild(thead);
311     table.appendChild(tbody);
07c714 312     
7ca6a1 313     switch(feature.getId().substr(0, 1)) {
JK 314         case 'v':
07c714 315             type = lang.type_vehicle;
JK 316             
317             if(!feature.get('vehicle_type')) {
318                 break;
7ca6a1 319             }
07c714 320             
JK 321             var span = displayVehicle(feature.get('vehicle_type'));
322             
323             additional = document.createElement('p');
324             setText(additional, span.title);
325             additional.insertBefore(span, additional.firstChild);
8b6250 326             
JK 327             addElementWithText(thead, 'th', lang.header_time);
328             addElementWithText(thead, 'th', lang.header_stop);
329             
330             vehicleTable(feature.get('tripId'), tbody);
7ca6a1 331         break;
07c714 332         case 's':
JK 333             type = lang.type_stop;
8b6250 334             
JK 335             addElementWithText(thead, 'th', lang.header_line);
336             addElementWithText(thead, 'th', lang.header_direction);
337             addElementWithText(thead, 'th', lang.header_time);
338             addElementWithText(thead, 'th', lang.header_delay);
339             
340             stopTable('stop', feature.get('shortName'), tbody);
07c714 341         break;
JK 342         case 'p':
343             type = lang.type_stoppoint;
8b6250 344             
JK 345             additional = document.createElement('p');
346             additional.className = 'small';
347             addElementWithText(additional, 'a', lang.departures_for_stop).addEventListener(
348                 'click',
349                 function() {
350                     featureClicked(stops_source.forEachFeature(function(stop_feature) {
351                         if(stop_feature.get('shortName') == feature.get('shortName')) {
352                             return stop_feature;
353                         }
354                     }));
355                 }
356             );
357             
358             addElementWithText(thead, 'th', lang.header_line);
359             addElementWithText(thead, 'th', lang.header_direction);
360             addElementWithText(thead, 'th', lang.header_time);
361             addElementWithText(thead, 'th', lang.header_delay);
362             
363             stopTable('stopPoint', feature.get('stopPoint'), tbody);
07c714 364         break;
JK 365     }
8b6250 366     
JK 367     var loader = addElementWithText(tbody, 'td', lang.loading);
368     loader.className = 'active';
369     loader.colspan = thead.childNodes.length;
07c714 370     
JK 371     addParaWithText(popup_element, type).className = 'type';
372     addParaWithText(popup_element, name).className = 'name';
373     
374     if(additional) {
375         popup_element.appendChild(additional);
7ca6a1 376     }
JK 377     
8b6250 378     popup_element.appendChild(table);
JK 379     
7ca6a1 380     ignore_hashchange = true;
JK 381     window.location.hash = '#!' + feature.getId();
382     
07c714 383     map.getView().animate({
JK 384         center: feature.getGeometry().getCoordinates(),
385     });
386     
387     $(popup_element).addClass('show');
388     
389     feature_id = feature.getId();
7ca6a1 390 }
JK 391
392 function hash() {
393     if(ignore_hashchange) {
394         ignore_hashchange = false;
395         return;
396     }
397     
398     var tramId = null;
399     
400     var vehicleId = null;
401     var stopId = null;
402     var stopPointId = null;
403     
404     var feature = null;
405     
406     if(window.location.hash.match(/^#!t[0-9]{3}$/)) {
407         tramId = parseInt(window.location.hash.substr(3));
408     } else if(window.location.hash.match(/^#![A-Za-z]{2}[0-9]{3}$/)) {
409         tramId = parseInt(window.location.hash.substr(4));
410     } else if(window.location.hash.match(/^#!v[0-9]+$/)) {
411         vehicleId = window.location.hash.substr(3);
412     } else if(window.location.hash.match(/^#!s[0-9]+$/)) {
413         stopId = window.location.hash.substr(3);
414     } else if(window.location.hash.match(/^#!p[0-9]+$/)) {
415         stopPointId = window.location.hash.substr(3);
416     }
417     
418     if(tramId) {
419         vehicleId = tramIdToVehicleId(tramId);
420     }
421     
422     if(vehicleId) {
423         feature = vehicles_source.getFeatureById('v' + vehicleId);
424     } else if(stopId) {
425         feature = stops_source.getFeatureById('s' + stopId);
426     } else if(stopPointId) {
427         feature = stop_points_source.getFeatureById('p' + stopPointId);
428     }
429     
430     featureClicked(feature);
57b8d3 431 }
JK 432
433 function init() {
434     if(!window.jQuery) {
435         fail(lang.jquery_not_loaded);
436         return;
437     }
438     
439     $.ajaxSetup({
440         dataType: 'json',
441         timeout: 10000,
442     });
443     
444     stops_source = new ol.source.Vector({
445         features: [],
446     });
447     stops_layer = new ol.layer.Vector({
448         source: stops_source,
449     });
450     
451     stop_points_source = new ol.source.Vector({
452         features: [],
453     });
454     stop_points_layer = new ol.layer.Vector({
455         source: stop_points_source,
456         visible: false,
457     });
458     
459     vehicles_source = new ol.source.Vector({
460         features: [],
461     });
462     vehicles_layer = new ol.layer.Vector({
463         source: vehicles_source,
464     });
465     
466     map = new ol.Map({
467         target: 'map',
468         layers: [
469             new ol.layer.Tile({
470                 source: new ol.source.OSM()
471             }),
472             stops_layer,
473             stop_points_layer,
474             vehicles_layer,
475         ],
476         view: new ol.View({
477             center: ol.proj.fromLonLat([19.94, 50.06]),
478             zoom: 13
479         }),
480         controls: ol.control.defaults({
481             attributionOptions: ({
482                 collapsible: false,
483             })
484         }).extend([
485             new ol.control.Control({
486                 element: document.getElementById('title'),
487             }),
488             new ol.control.Control({
489                 element: fail_element,
490             })
491         ]),
492     });
493     
494     // Display popup on click
495     map.on('singleclick', function(e) {
496         var feature = map.forEachFeatureAtPixel(e.pixel, function(feature) { return feature; });
7ca6a1 497         featureClicked(feature);
57b8d3 498     });
JK 499
500     // Change mouse cursor when over marker
501     map.on('pointermove', function(e) {
502         var hit = map.hasFeatureAtPixel(e.pixel);
503         var target = map.getTargetElement();
504         target.style.cursor = hit ? 'pointer' : '';
505     });
506     
507     // Change layer visibility on zoom
508     map.getView().on('change:resolution', function(e) {
509         if(map.getView().getZoom() >= 16) {
510             stops_layer.setVisible(false);
511             stop_points_layer.setVisible(true);
512         } else {
513             stops_layer.setVisible(true);
514             stop_points_layer.setVisible(false);
515         }
516     });
517     
7ca6a1 518     $.when(
JK 519         updateVehicles(),
520         updateStops(),
521         updateStopPoints()
522     ).done(function() {
523         hash();
524     });
525     
526     window.addEventListener('hashchange', hash);
57b8d3 527     
JK 528     setTimeout(function() {
529         if(vehicles_xhr) vehicles_xhr.abort();
530         if(vehicles_timer) clearTimeout(vehicles_timer);
531           
532         fail(lang.error_refresh);
533     }, 1800000);
534 }
535
536 init();