Improved www.ttss.krakow.pl
Jacek Kowalski
2019-02-03 1c0616f0578a246beedbdbaf49a617b46f1e9380
commit | author | age
f4a54f 1 "use strict";
JK 2
f36d1f 3 var ttss_refresh = 10000; // 10 seconds
a8a6d1 4 var ttss_position_type = 'CORRECTED';
57b8d3 5
eafc1c 6 var trams_xhr = null;
JK 7 var trams_timer = null;
8 var trams_last_update = 0;
9 var trams_source = null;
10 var trams_layer = null;
11
12 var buses_xhr = null;
13 var buses_timer = null;
14 var buses_last_update = 0;
15 var buses_source = null;
16 var buses_layer = null;
17
f0bae0 18 var vehicles_info = {};
57b8d3 19
JK 20 var stops_xhr = null;
f4a54f 21 var stops_buses_source = null;
JK 22 var stops_buses_layer = null;
23 var stops_trams_source = null;
24 var stops_trams_layer = null;
25 var stop_points_buses_source = null;
26 var stop_points_buses_layer = null;
27 var stop_points_trams_source = null;
28 var stop_points_trams_layer = null;
29
30 var stop_selected_source = null;
31 var stop_selected_layer = null;
57b8d3 32
1d4785 33 var feature_clicked = null;
8b6250 34 var feature_xhr = null;
JK 35 var feature_timer = null;
1d4785 36
JK 37 var route_source = null;
38 var route_layer = null;
07c714 39
57b8d3 40 var map = null;
JK 41 var popup_element = document.getElementById('popup');
9f0f6a 42 var popup_close_callback;
57b8d3 43 var fail_element = document.getElementById('fail');
JK 44
7ca6a1 45 var ignore_hashchange = false;
JK 46
57b8d3 47 function fail(msg) {
JK 48     setText(fail_element, msg);
49     fail_element.style.top = '0.5em';
50 }
51
8b6250 52 function fail_popup(msg) {
JK 53     addElementWithText(popup_element, 'p', msg).className = 'error';
54 }
55
56 function fail_ajax_generic(data, fnc) {
57b8d3 57     // abort() is not a failure
JK 58     if(data.readyState == 0 && data.statusText == 'abort') return;
59     
60     if(data.status == 0) {
8b6250 61         fnc(lang.error_request_failed_connectivity, data);
57b8d3 62     } else if (data.statusText) {
8b6250 63         fnc(lang.error_request_failed_status.replace('$status', data.statusText), data);
57b8d3 64     } else {
8b6250 65         fnc(lang.error_request_failed, data);
57b8d3 66     }
8b6250 67 }
JK 68
69 function fail_ajax(data) {
70     fail_ajax_generic(data, fail);
71 }
72
73 function fail_ajax_popup(data) {
74     fail_ajax_generic(data, fail_popup);
57b8d3 75 }
JK 76
77 function getGeometry(object) {
07c714 78     return new ol.geom.Point(ol.proj.fromLonLat([object.longitude / 3600000.0, object.latitude / 3600000.0]));
57b8d3 79 }
JK 80
1d4785 81 function styleVehicle(vehicle, selected) {
JK 82     var color_type = 'black';
83     if(vehicle.get('vehicle_type')) {
84         switch(vehicle.get('vehicle_type').low) {
125626 85             case 0:
1d4785 86                 color_type = 'orange';
JK 87             break;
125626 88             case 1:
JK 89             case 2:
1d4785 90                 color_type = 'green';
JK 91             break;
92         }
93     }
94     
3d2caa 95     var fill = '#B70';
6cb525 96     if(vehicle.getId().startsWith('b')) {
JK 97         fill = '#05B';
98     }
99     if(selected) {
100         fill = '#292';
101     }
1d4785 102     
3d2caa 103     var image = '<svg xmlns="http://www.w3.org/2000/svg" height="30" width="20"><polygon points="10,0 20,23 0,23" style="fill:'+fill+';stroke:'+color_type+';stroke-width:3" /></svg>';
1d4785 104     
f4a54f 105     vehicle.setStyle(new ol.style.Style({
1d4785 106         image: new ol.style.Icon({
JK 107             src: 'data:image/svg+xml;base64,' + btoa(image),
a8a6d1 108             rotation: Math.PI * parseFloat(vehicle.get('heading') ? vehicle.get('heading') : 0) / 180.0,
1d4785 109         }),
JK 110         text: new ol.style.Text({
111             font: 'bold 10px sans-serif',
112             text: vehicle.get('line'),
113             fill: new ol.style.Fill({color: 'white'}),
114         }),
f4a54f 115     }));
1d4785 116 }
JK 117
f4a54f 118 function markStops(stops, type, routeStyle) {
JK 119     stop_selected_source.clear();
ba6e87 120     
f4a54f 121     var style = stops_trams_layer.getStyle();
JK 122     if(type == 'b') {
123         style = stops_buses_layer.getStyle();
124     }
125     style = style.clone();
126     
127     if(routeStyle) {
128         style.getImage().setRadius(5);
129     } else {
130         style.getImage().getStroke().setWidth(2);
131         style.getImage().getStroke().setColor('#F00');
132         style.getImage().setRadius(5);
ba6e87 133     }
1d4785 134     
f4a54f 135     stop_selected_layer.setStyle(style);
JK 136     
137     var feature = null;
138     var prefix = null;
139     for(var i = 0; i < stops.length; i++) {
140         feature = null;
141         if(stops[i].getId) {
142             feature = stops[i];
143         } else {
144             prefix = stops[i].substr(0,2);
145             feature = null;
146             if(prefix == 'sb') {
147                 feature = stops_buses_source.getFeatureById(stops[i]);
148             } else if(prefix == 'st') {
149                 feature = stops_trams_source.getFeatureById(stops[i]);
150             } else if(prefix == 'pb') {
151                 feature = stop_points_buses_source.getFeatureById(stops[i]);
152             } else if(prefix == 'pt') {
153                 feature = stop_points_trams_source.getFeatureById(stops[i]);
154             }
155         }
156         if(feature) {
157             stop_selected_source.addFeature(feature);
158         }
1d4785 159     }
JK 160     
f4a54f 161     stop_selected_layer.setVisible(true);
1d4785 162 }
JK 163
164 function unstyleSelectedFeatures() {
f4a54f 165     stop_selected_source.clear();
JK 166     route_source.clear();
167     if(feature_clicked && (feature_clicked.getId().startsWith('t') || feature_clicked.getId().startsWith('b'))) {
168         styleVehicle(feature_clicked);
1d4785 169     }
JK 170 }
171
eafc1c 172 function updateTrams() {
JK 173     if(trams_timer) clearTimeout(trams_timer);
174     if(trams_xhr) trams_xhr.abort();
175     trams_xhr = $.get(
176         ttss_trams_base + '/geoserviceDispatcher/services/vehicleinfo/vehicles'
a8a6d1 177             + '?positionType=' + ttss_position_type
57b8d3 178             + '&colorType=ROUTE_BASED'
eafc1c 179             + '&lastUpdate=' + encodeURIComponent(trams_last_update)
57b8d3 180     ).done(function(data) {
eafc1c 181         trams_last_update = data.lastUpdate;
57b8d3 182         
JK 183         for(var i = 0; i < data.vehicles.length; i++) {
184             var vehicle = data.vehicles[i];
185             
eafc1c 186             var vehicle_feature = trams_source.getFeatureById('t' + vehicle.id);
57b8d3 187             if(vehicle.isDeleted) {
JK 188                 if(vehicle_feature) {
eafc1c 189                     trams_source.removeFeature(vehicle_feature);
745cfd 190                     if(feature_clicked && feature_clicked.getId() === vehicle_feature.getId()) {
07c714 191                         featureClicked();
57b8d3 192                     }
JK 193                 }
194                 continue;
195             }
196             
197             var vehicle_name_space = vehicle.name.indexOf(' ');
198             vehicle.line = vehicle.name.substr(0, vehicle_name_space);
199             vehicle.direction = vehicle.name.substr(vehicle_name_space+1);
200             if(special_directions[vehicle.direction]) {
201                 vehicle.line = special_directions[vehicle.direction];
202             }
203             
204             vehicle.geometry = getGeometry(vehicle);
3d2caa 205             vehicle.vehicle_type = parseVehicle('t' + vehicle.id);
57b8d3 206             
JK 207             if(!vehicle_feature) {
208                 vehicle_feature = new ol.Feature(vehicle);
eafc1c 209                 vehicle_feature.setId('t' + vehicle.id);
57b8d3 210                 
f4a54f 211                 styleVehicle(vehicle_feature);
eafc1c 212                 trams_source.addFeature(vehicle_feature);
57b8d3 213             } else {
JK 214                 vehicle_feature.setProperties(vehicle);
a8a6d1 215                 vehicle_feature.getStyle().getImage().setRotation(Math.PI * parseFloat(vehicle.heading ? vehicle.heading : 0) / 180.0);
57b8d3 216             }
JK 217         }
218         
eafc1c 219         trams_timer = setTimeout(function() {
JK 220             updateTrams();
57b8d3 221         }, ttss_refresh);
JK 222     }).fail(fail_ajax);
7ca6a1 223     
eafc1c 224     return trams_xhr;
JK 225 }
226
227 function updateBuses() {
228     if(buses_timer) clearTimeout(buses_timer);
229     if(buses_xhr) buses_xhr.abort();
230     
231     buses_xhr = $.get(
232         ttss_buses_base + '/geoserviceDispatcher/services/vehicleinfo/vehicles'
8a0cf8 233             + '?positionType=RAW'
eafc1c 234             + '&colorType=ROUTE_BASED'
JK 235             + '&lastUpdate=' + encodeURIComponent(buses_last_update)
236     ).done(function(data) {
237         buses_last_update = data.lastUpdate;
238         
239         for(var i = 0; i < data.vehicles.length; i++) {
240             var vehicle = data.vehicles[i];
241             
242             var vehicle_feature = buses_source.getFeatureById('b' + vehicle.id);
326ad3 243             if(vehicle.isDeleted || !vehicle.latitude || !vehicle.longitude) {
eafc1c 244                 if(vehicle_feature) {
JK 245                     buses_source.removeFeature(vehicle_feature);
246                     if(feature_clicked && feature_clicked.getId() === vehicle_feature.getId()) {
247                         featureClicked();
248                     }
249                 }
250                 continue;
251             }
252             
253             var vehicle_name_space = vehicle.name.indexOf(' ');
254             vehicle.line = vehicle.name.substr(0, vehicle_name_space);
255             vehicle.direction = vehicle.name.substr(vehicle_name_space+1);
256             if(special_directions[vehicle.direction]) {
257                 vehicle.line = special_directions[vehicle.direction];
258             }
259             
260             vehicle.geometry = getGeometry(vehicle);
3d2caa 261             vehicle.vehicle_type = parseVehicle('b' + vehicle.id);
eafc1c 262             
JK 263             if(!vehicle_feature) {
264                 vehicle_feature = new ol.Feature(vehicle);
265                 vehicle_feature.setId('b' + vehicle.id);
266                 
f4a54f 267                 styleVehicle(vehicle_feature);
eafc1c 268                 buses_source.addFeature(vehicle_feature);
JK 269             } else {
270                 vehicle_feature.setProperties(vehicle);
271                 vehicle_feature.getStyle().getImage().setRotation(Math.PI * parseFloat(vehicle.heading) / 180.0);
272             }
273         }
274         
275         buses_timer = setTimeout(function() {
276             updateBuses();
277         }, ttss_refresh);
278     }).fail(fail_ajax);
279     
280     return buses_xhr;
57b8d3 281 }
JK 282
283 function updateStopSource(stops, prefix, source) {
284     for(var i = 0; i < stops.length; i++) {
285         var stop = stops[i];
e61357 286         
JK 287         if(stop.category == 'other') continue;
288         
57b8d3 289         stop.geometry = getGeometry(stop);
JK 290         var stop_feature = new ol.Feature(stop);
291         
292         stop_feature.setId(prefix + stop.id);
293         
294         source.addFeature(stop_feature);
295     }
296 }
297
f4a54f 298 function updateStops(base, suffix, source) {
7ca6a1 299     return $.get(
eafc1c 300         base + '/geoserviceDispatcher/services/stopinfo/stops'
57b8d3 301             + '?left=-648000000'
JK 302             + '&bottom=-324000000'
303             + '&right=648000000'
304             + '&top=324000000'
305     ).done(function(data) {
f4a54f 306         updateStopSource(data.stops, 's' + suffix, source);
57b8d3 307     }).fail(fail_ajax);
JK 308 }
309
f4a54f 310 function updateStopPoints(base, suffix, source) {
7ca6a1 311     return $.get(
eafc1c 312         base + '/geoserviceDispatcher/services/stopinfo/stopPoints'
57b8d3 313             + '?left=-648000000'
JK 314             + '&bottom=-324000000'
315             + '&right=648000000'
316             + '&top=324000000'
317     ).done(function(data) {
f4a54f 318         updateStopSource(data.stopPoints, 'p' + suffix, source);
57b8d3 319     }).fail(fail_ajax);
7ca6a1 320 }
JK 321
eafc1c 322 function vehicleTable(tripId, table, featureId) {
8b6250 323     if(feature_xhr) feature_xhr.abort();
JK 324     if(feature_timer) clearTimeout(feature_timer);
325     
eafc1c 326     var url = ttss_trams_base;
JK 327     if(featureId.startsWith('b')) {
328         url = ttss_buses_base;
329     }
330     
331     var vehicleId = featureId.substr(1);
332     
8b6250 333     feature_xhr = $.get(
eafc1c 334         url + '/services/tripInfo/tripPassages'
8b6250 335             + '?tripId=' + encodeURIComponent(tripId)
JK 336             + '&mode=departure'
337     ).done(function(data) {
98ba34 338         if(!data.routeName || !data.directionText) {
8b6250 339             return;
JK 340         }
341         
342         deleteChildren(table);
343         
344         for(var i = 0, il = data.old.length; i < il; i++) {
345             var tr = document.createElement('tr');
346             addCellWithText(tr, data.old[i].actualTime || data.old[i].plannedTime);
347             addCellWithText(tr, data.old[i].stop_seq_num + '. ' + data.old[i].stop.name);
348             
349             tr.className = 'active';
350             table.appendChild(tr);
351         }
352         
f4a54f 353         var stopsToMark = [];
1d4785 354         
8b6250 355         for(var i = 0, il = data.actual.length; i < il; i++) {
JK 356             var tr = document.createElement('tr');
357             addCellWithText(tr, data.actual[i].actualTime || data.actual[i].plannedTime);
358             addCellWithText(tr, data.actual[i].stop_seq_num + '. ' + data.actual[i].stop.name);
1d4785 359             
f4a54f 360             stopsToMark.push('s' + featureId.substr(0,1) + data.actual[i].stop.id);
8b6250 361             
JK 362             if(data.actual[i].status == 'STOPPING') {
363                 tr.className = 'success';
364             }
365             table.appendChild(tr);
366         }
f4a54f 367         
JK 368         markStops(stopsToMark, featureId.substr(0,1), true);
8b6250 369         
eafc1c 370         feature_timer = setTimeout(function() { vehicleTable(tripId, table, featureId); }, ttss_refresh);
1d4785 371         
JK 372         if(!vehicleId) return;
373            
374         feature_xhr = $.get(
eafc1c 375             url + '/geoserviceDispatcher/services/pathinfo/vehicle'
1d4785 376                 + '?id=' + encodeURIComponent(vehicleId)
JK 377         ).done(function(data) {
378             if(!data || !data.paths || !data.paths[0] || !data.paths[0].wayPoints) return;
379             
380             var point = null;
381             var points = [];
382             for(var i = 0; i < data.paths[0].wayPoints.length; i++) {
383                 point = data.paths[0].wayPoints[i];
384                 points.push(ol.proj.fromLonLat([
385                     point.lon / 3600000.0,
386                     point.lat / 3600000.0,
387                 ]));
388             }
389             
390             route_source.addFeature(new ol.Feature({
391                 geometry: new ol.geom.LineString(points)
392             }));
f4a54f 393             route_layer.setVisible(true);
1d4785 394         });
8b6250 395     }).fail(fail_ajax_popup);
JK 396 }
397
eafc1c 398 function stopTable(stopType, stopId, table, featureId) {
8b6250 399     if(feature_xhr) feature_xhr.abort();
JK 400     if(feature_timer) clearTimeout(feature_timer);
401     
eafc1c 402     var url = ttss_trams_base;
JK 403     if(featureId.substr(1,1) == 'b') {
404         url = ttss_buses_base;
405     }
406     
8b6250 407     feature_xhr = $.get(
eafc1c 408         url + '/services/passageInfo/stopPassages/' + stopType
8b6250 409             + '?' + stopType + '=' + encodeURIComponent(stopId)
JK 410             + '&mode=departure'
411     ).done(function(data) {
412         deleteChildren(table);
413         
414         for(var i = 0, il = data.old.length; i < il; i++) {
415             var tr = document.createElement('tr');
416             addCellWithText(tr, data.old[i].patternText);
417             var dir_cell = addCellWithText(tr, data.old[i].direction);
418             var vehicle = parseVehicle(data.old[i].vehicleId);
419             dir_cell.appendChild(displayVehicle(vehicle));
420             var status = parseStatus(data.old[i]);
421             addCellWithText(tr, status);
422             addCellWithText(tr, '');
423             
424             tr.className = 'active';
425             table.appendChild(tr);
426         }
427         
428         for(var i = 0, il = data.actual.length; i < il; i++) {
429             var tr = document.createElement('tr');
430             addCellWithText(tr, data.actual[i].patternText);
431             var dir_cell = addCellWithText(tr, data.actual[i].direction);
432             var vehicle = parseVehicle(data.actual[i].vehicleId);
433             dir_cell.appendChild(displayVehicle(vehicle));
434             var status = parseStatus(data.actual[i]);
435             var status_cell = addCellWithText(tr, status);
436             var delay = parseDelay(data.actual[i]);
437             var delay_cell = addCellWithText(tr, delay);
438             
439             if(status == lang.boarding_sign) {
440                 tr.className = 'success';
441                 status_cell.className = 'status-boarding';
442             } else if(parseInt(delay) > 9) {
443                 tr.className = 'danger';
444                 delay_cell.className = 'status-delayed';
445             } else if(parseInt(delay) > 3) {
446                 tr.className = 'warning';
447             }
448             
449             table.appendChild(tr);
450         }
451         
eafc1c 452         feature_timer = setTimeout(function() { stopTable(stopType, stopId, table, featureId); }, ttss_refresh);
8b6250 453     }).fail(fail_ajax_popup);
JK 454 }
455
9f0f6a 456 function showPanel(contents, closeCallback) {
JK 457     var old_callback = popup_close_callback;
458     popup_close_callback = null;
459     if(old_callback) old_callback();
460     popup_close_callback = closeCallback;
461     
462     deleteChildren(popup_element);
463     
464     var close = addParaWithText(popup_element, '×');
465     close.className = 'close';
466     close.addEventListener('click', function() { hidePanel(); });
467     
468     popup_element.appendChild(contents);
469     
470     $(popup_element).addClass('show');
471 }
472
473 function hidePanel() {
474     var old_callback = popup_close_callback;
475     popup_close_callback = null;
476     if(old_callback) old_callback();
477     
478     $(popup_element).removeClass('show');
479 }
480
7ca6a1 481 function featureClicked(feature) {
1d4785 482     if(feature && !feature.getId()) return;
JK 483     
484     unstyleSelectedFeatures();
485     
7ca6a1 486     if(!feature) {
9f0f6a 487         hidePanel();
7ca6a1 488         return;
JK 489     }
490     
491     var coordinates = feature.getGeometry().getCoordinates();
492     
9f0f6a 493     var div = document.createElement('div');
8b6250 494     
07c714 495     var type;
JK 496     var name = feature.get('name');
497     var additional;
8b6250 498     var table = document.createElement('table');
JK 499     var thead = document.createElement('thead');
500     var tbody = document.createElement('tbody');
501     table.appendChild(thead);
502     table.appendChild(tbody);
07c714 503     
7ca6a1 504     switch(feature.getId().substr(0, 1)) {
eafc1c 505         case 't':
JK 506         case 'b':
a83099 507             type = lang.type_tram;
JK 508             if(feature.getId().startsWith('b')) {
509                 type = lang.type_bus;
510             }
07c714 511             
JK 512             var span = displayVehicle(feature.get('vehicle_type'));
513             
514             additional = document.createElement('p');
439d60 515             if(span.title) {
JK 516                 setText(additional, span.title);
f0bae0 517             } else {
JK 518                 setText(additional, feature.getId());
439d60 519             }
07c714 520             additional.insertBefore(span, additional.firstChild);
8b6250 521             
JK 522             addElementWithText(thead, 'th', lang.header_time);
523             addElementWithText(thead, 'th', lang.header_stop);
524             
eafc1c 525             vehicleTable(feature.get('tripId'), tbody, feature.getId());
f4a54f 526             
JK 527             styleVehicle(feature, true);
7ca6a1 528         break;
07c714 529         case 's':
a83099 530             type = lang.type_stop_tram;
JK 531             if(feature.getId().startsWith('sb')) {
532                 type = lang.type_stop_bus;
533             }
8b6250 534             
JK 535             addElementWithText(thead, 'th', lang.header_line);
536             addElementWithText(thead, 'th', lang.header_direction);
537             addElementWithText(thead, 'th', lang.header_time);
538             addElementWithText(thead, 'th', lang.header_delay);
539             
eafc1c 540             stopTable('stop', feature.get('shortName'), tbody, feature.getId());
f4a54f 541             markStops([feature], feature.getId().substr(1,1));
07c714 542         break;
JK 543         case 'p':
a83099 544             type = lang.type_stoppoint_tram;
JK 545             if(feature.getId().startsWith('pb')) {
546                 type = lang.type_stoppoint_bus;
547             }
8b6250 548             
JK 549             additional = document.createElement('p');
550             additional.className = 'small';
551             addElementWithText(additional, 'a', lang.departures_for_stop).addEventListener(
552                 'click',
553                 function() {
f7f4ce 554                     var stops_source = stops_trams_source;
JK 555                     if(feature.getId().startsWith('pb')) {
556                         stops_source = stops_buses_source;
557                     }
8b6250 558                     featureClicked(stops_source.forEachFeature(function(stop_feature) {
a83099 559                         if(stop_feature.get('shortName') == feature.get('shortName') && stop_feature.getId().substr(1,1) == feature.getId().substr(1,1)) {
8b6250 560                             return stop_feature;
JK 561                         }
562                     }));
563                 }
564             );
565             
566             addElementWithText(thead, 'th', lang.header_line);
567             addElementWithText(thead, 'th', lang.header_direction);
568             addElementWithText(thead, 'th', lang.header_time);
569             addElementWithText(thead, 'th', lang.header_delay);
570             
eafc1c 571             stopTable('stopPoint', feature.get('stopPoint'), tbody, feature.getId());
f4a54f 572             markStops([feature], feature.getId().substr(1,1));
07c714 573         break;
JK 574     }
8b6250 575     
JK 576     var loader = addElementWithText(tbody, 'td', lang.loading);
577     loader.className = 'active';
ee4e7c 578     loader.colSpan = thead.childNodes.length;
07c714 579     
9f0f6a 580     addParaWithText(div, type).className = 'type';
JK 581     addParaWithText(div, name).className = 'name';
07c714 582     
JK 583     if(additional) {
9f0f6a 584         div.appendChild(additional);
7ca6a1 585     }
JK 586     
9f0f6a 587     div.appendChild(table);
7ca6a1 588     
1d4785 589     setTimeout(function () {map.getView().animate({
07c714 590         center: feature.getGeometry().getCoordinates(),
1d4785 591     }) }, 10);
07c714 592     
9f0f6a 593     ignore_hashchange = true;
JK 594     window.location.hash = '#!' + feature.getId();
595     
596     showPanel(div, function() {
597         if(!ignore_hashchange) {
598             ignore_hashchange = true;
599             window.location.hash = '';
600             
601             feature_clicked = null;
602             unstyleSelectedFeatures();
603             
604             if(feature_xhr) feature_xhr.abort();
605             if(feature_timer) clearTimeout(feature_timer);
606         }
607     });
07c714 608     
1d4785 609     feature_clicked = feature;
7ca6a1 610 }
JK 611
612 function hash() {
613     if(ignore_hashchange) {
614         ignore_hashchange = false;
615         return;
616     }
617     
618     var feature = null;
f4a54f 619     var vehicleId = null;
JK 620     var stopId = null;
7ca6a1 621     
JK 622     if(window.location.hash.match(/^#!t[0-9]{3}$/)) {
f4a54f 623         vehicleId = depotIdToVehicleId(window.location.hash.substr(3), 't');
JK 624     } else if(window.location.hash.match(/^#!b[0-9]{3}$/)) {
625         vehicleId = depotIdToVehicleId(window.location.hash.substr(3), 'b');
7ca6a1 626     } else if(window.location.hash.match(/^#![A-Za-z]{2}[0-9]{3}$/)) {
f4a54f 627         vehicleId = depotIdToVehicleId(window.location.hash.substr(2));
439d60 628     } else if(window.location.hash.match(/^#!v-?[0-9]+$/)) {
f4a54f 629         vehicleId = 't' + window.location.hash.substr(3);
JK 630     } else if(window.location.hash.match(/^#![tb]-?[0-9]+$/)) {
631         vehicleId = window.location.hash.substr(2);
632     } else if(window.location.hash.match(/^#![sp]-?[0-9]+$/)) {
633         stopId = window.location.hash.substr(2,1) + 't' + window.location.hash.substr(3);
634     } else if(window.location.hash.match(/^#![sp][tb]-?[0-9]+$/)) {
635         stopId = window.location.hash.substr(2);
a8a6d1 636     } else if(window.location.hash == '#!RAW') {
JK 637         ttss_position_type = 'RAW';
7ca6a1 638     }
JK 639     
f4a54f 640     if(vehicleId) {
JK 641         if(vehicleId.startsWith('b')) {
642             feature = buses_source.getFeatureById(vehicleId);
643         } else {
644             feature = trams_source.getFeatureById(vehicleId);
645         }
7ca6a1 646     } else if(stopId) {
f4a54f 647         if(stopId.startsWith('st')) {
JK 648             feature = stops_trams_source.getFeatureById(stopId);
649         } else if(stopId.startsWith('sb')) {
650             feature = stops_buses_source.getFeatureById(stopId);
651         } else if(stopId.startsWith('pt')) {
652             feature = stop_points_trams_source.getFeatureById(stopId);
653         } else if(stopId.startsWith('pb')) {
654             feature = stop_points_buses_source.getFeatureById(stopId);
655         }
7ca6a1 656     }
JK 657     
658     featureClicked(feature);
57b8d3 659 }
JK 660
0e60d1 661 function getDistance(c1, c2) {
JK 662     if(c1.getGeometry) {
663         c1 = c1.getGeometry().getCoordinates();
664     }
665     if(c2.getGeometry) {
666         c2 = c2.getGeometry().getCoordinates();
667     }
668     
669     var c1 = ol.proj.transform(c1, 'EPSG:3857', 'EPSG:4326');
670     var c2 = ol.proj.transform(c2, 'EPSG:3857', 'EPSG:4326');
a8a6d1 671     return ol.sphere.getDistance(c1, c2);
0e60d1 672 }
JK 673
674 function returnClosest(point, f1, f2) {
675     if(!f1) return f2;
676     if(!f2) return f1;
677     
678     return (getDistance(point, f1) < getDistance(point, f2)) ? f1 : f2;
679 }
680
57b8d3 681 function init() {
JK 682     if(!window.jQuery) {
683         fail(lang.jquery_not_loaded);
684         return;
685     }
686     
687     $.ajaxSetup({
688         dataType: 'json',
689         timeout: 10000,
690     });
691     
f4a54f 692     stops_buses_source = new ol.source.Vector({
57b8d3 693         features: [],
JK 694     });
f4a54f 695     stops_buses_layer = new ol.layer.Vector({
JK 696         source: stops_buses_source,
697         renderMode: 'image',
698         style: new ol.style.Style({
699             image: new ol.style.Circle({
700                 fill: new ol.style.Fill({color: '#07F'}),
701                 stroke: new ol.style.Stroke({color: '#05B', width: 1}),
702                 radius: 3,
703             }),
704         }),
57b8d3 705     });
JK 706     
f4a54f 707     stops_trams_source = new ol.source.Vector({
57b8d3 708         features: [],
JK 709     });
f4a54f 710     stops_trams_layer = new ol.layer.Vector({
JK 711         source: stops_trams_source,
712         renderMode: 'image',
713         style: new ol.style.Style({
714             image: new ol.style.Circle({
715                 fill: new ol.style.Fill({color: '#FA0'}),
716                 stroke: new ol.style.Stroke({color: '#B70', width: 1}),
717                 radius: 3,
718             }),
719         }),
720     });
721     
722     stop_points_buses_source = new ol.source.Vector({
723         features: [],
724     });
725     stop_points_buses_layer = new ol.layer.Vector({
726         source: stop_points_buses_source,
727         renderMode: 'image',
728         visible: false,
729         style: new ol.style.Style({
730             image: new ol.style.Circle({
731                 fill: new ol.style.Fill({color: '#07F'}),
732                 stroke: new ol.style.Stroke({color: '#05B', width: 2}),
733                 radius: 3,
734             }),
735         }),
736     });
737     
738     stop_points_trams_source = new ol.source.Vector({
739         features: [],
740     });
741     stop_points_trams_layer = new ol.layer.Vector({
742         source: stop_points_trams_source,
743         renderMode: 'image',
744         visible: false,
745         style: new ol.style.Style({
746             image: new ol.style.Circle({
747                 fill: new ol.style.Fill({color: '#FA0'}),
748                 stroke: new ol.style.Stroke({color: '#B70', width: 2}),
749                 radius: 3,
750             }),
751         }),
752     });
753     
754     stop_selected_source = new ol.source.Vector({
755         features: [],
756     });
757     stop_selected_layer = new ol.layer.Vector({
758         source: stop_selected_source,
57b8d3 759         visible: false,
JK 760     });
761     
eafc1c 762     trams_source = new ol.source.Vector({
57b8d3 763         features: [],
JK 764     });
eafc1c 765     trams_layer = new ol.layer.Vector({
JK 766         source: trams_source,
767     });
768     
769     buses_source = new ol.source.Vector({
770         features: [],
771     });
772     buses_layer = new ol.layer.Vector({
773         source: buses_source,
57b8d3 774     });
JK 775     
1d4785 776     route_source = new ol.source.Vector({
JK 777         features: [],
778     });
779     route_layer = new ol.layer.Vector({
780         source: route_source,
781         style: new ol.style.Style({
782             stroke: new ol.style.Stroke({ color: [255, 153, 0, .8], width: 5 })
783         }),
784     });
785     
1c0616 786     ol.style.IconImageCache.shared.setSize(512);
JK 787     
57b8d3 788     map = new ol.Map({
JK 789         target: 'map',
790         layers: [
791             new ol.layer.Tile({
792                 source: new ol.source.OSM()
793             }),
1d4785 794             route_layer,
f4a54f 795             stops_buses_layer,
JK 796             stops_trams_layer,
797             stop_points_buses_layer,
798             stop_points_trams_layer,
799             stop_selected_layer,
eafc1c 800             buses_layer,
JK 801             trams_layer,
57b8d3 802         ],
JK 803         view: new ol.View({
804             center: ol.proj.fromLonLat([19.94, 50.06]),
f4a54f 805             zoom: 14
57b8d3 806         }),
JK 807         controls: ol.control.defaults({
808             attributionOptions: ({
809                 collapsible: false,
810             })
811         }).extend([
812             new ol.control.Control({
813                 element: document.getElementById('title'),
814             }),
815             new ol.control.Control({
816                 element: fail_element,
817             })
818         ]),
f4a54f 819         loadTilesWhileAnimating: false,
57b8d3 820     });
JK 821     
822     // Display popup on click
823     map.on('singleclick', function(e) {
0e60d1 824         var point = e.coordinate;
9f0f6a 825         var features = [];
f4a54f 826         map.forEachFeatureAtPixel(e.pixel, function(feature, layer) {
JK 827             if(layer == stop_selected_layer) return;
828             if(feature.getId()) features.push(feature);
829         });
0e60d1 830         
9f0f6a 831         if(features.length > 1) {
f4a54f 832             featureClicked();
JK 833             
9f0f6a 834             var div = document.createElement('div');
JK 835             
836             addParaWithText(div, lang.select_feature);
837             
838             for(var i = 0; i < features.length; i++) {
839                 var feature = features[i];
840                 
841                 var p = document.createElement('p');
842                 var a = document.createElement('a');
843                 p.appendChild(a);
844                 a.addEventListener('click', function(feature) { return function() {
845                     featureClicked(feature);
846                 }}(feature));
847                 
848                 var type = '';
849                 switch(feature.getId().substr(0, 1)) {
eafc1c 850                     case 't':
JK 851                     case 'b':
a83099 852                         type = lang.type_tram;
JK 853                         if(feature.getId().startsWith('b')) {
854                             type = lang.type_bus;
855                         }
439d60 856                         if(feature.get('vehicle_type').num) {
JK 857                             type += ' ' + feature.get('vehicle_type').num;
858                         }
9f0f6a 859                     break;
JK 860                     case 's':
a83099 861                         type = lang.type_stop_tram;
JK 862                         if(feature.getId().startsWith('sb')) {
863                             type = lang.type_stop_bus;
864                         }
9f0f6a 865                     break;
JK 866                     case 'p':
a83099 867                         type = lang.type_stoppoint_tram;
JK 868                         if(feature.getId().startsWith('pb')) {
869                             type = lang.type_stoppoint_bus;
870                         }
9f0f6a 871                     break;
JK 872                 }
873                 
874                 addElementWithText(a, 'span', type).className = 'small';
875                 a.appendChild(document.createTextNode(' '));
876                 addElementWithText(a, 'span', feature.get('name'));
877                 
878                 div.appendChild(p);
879             }
880             
881             showPanel(div);
882             
883             return;
884         }
885         
886         var feature = features[0];
0e60d1 887         if(!feature) {
f4a54f 888             if(stops_buses_layer.getVisible()) {
JK 889                 feature = returnClosest(point, feature, stops_buses_source.getClosestFeatureToCoordinate(point));
0e60d1 890             }
f4a54f 891             if(stops_trams_layer.getVisible()) {
JK 892                 feature = returnClosest(point, feature, stops_trams_source.getClosestFeatureToCoordinate(point));
893             }
894             if(stop_points_buses_layer.getVisible()) {
895                 feature = returnClosest(point, feature, stop_points_buses_source.getClosestFeatureToCoordinate(point));
896             }
897             if(stop_points_trams_layer.getVisible()) {
898                 feature = returnClosest(point, feature, stop_points_trams_source.getClosestFeatureToCoordinate(point));
0e60d1 899             }
eafc1c 900             if(trams_layer.getVisible()) {
JK 901                 feature = returnClosest(point, feature, trams_source.getClosestFeatureToCoordinate(point));
902             }
903             if(buses_layer.getVisible()) {
904                 feature = returnClosest(point, feature, buses_source.getClosestFeatureToCoordinate(point));
0e60d1 905             }
JK 906             
f4a54f 907             if(getDistance(point, feature) > map.getView().getResolution() * 20) {
0e60d1 908                 feature = null;
JK 909             }
910         }
911         
7ca6a1 912         featureClicked(feature);
57b8d3 913     });
9f0f6a 914     
JK 915     fail_element.addEventListener('click', function() {
916         fail_element.style.top = '-10em';
917     });
f0bae0 918     
57b8d3 919     // Change mouse cursor when over marker
JK 920     map.on('pointermove', function(e) {
921         var hit = map.hasFeatureAtPixel(e.pixel);
922         var target = map.getTargetElement();
923         target.style.cursor = hit ? 'pointer' : '';
924     });
925     
926     // Change layer visibility on zoom
927     map.getView().on('change:resolution', function(e) {
f4a54f 928         stop_points_buses_layer.setVisible(map.getView().getZoom() >= 16);
JK 929         stop_points_trams_layer.setVisible(map.getView().getZoom() >= 16);
57b8d3 930     });
JK 931     
7ca6a1 932     $.when(
3d2caa 933         updateVehicleInfo(),
eafc1c 934         updateTrams(),
JK 935         updateBuses(),
f4a54f 936         updateStops(ttss_trams_base, 't', stops_trams_source),
JK 937         updateStops(ttss_buses_base, 'b', stops_buses_source),
938         updateStopPoints(ttss_trams_base, 't', stop_points_trams_source),
939         updateStopPoints(ttss_buses_base, 'b', stop_points_buses_source),
7ca6a1 940     ).done(function() {
JK 941         hash();
942     });
943     
944     window.addEventListener('hashchange', hash);
57b8d3 945     
JK 946     setTimeout(function() {
eafc1c 947         if(trams_xhr) trams_xhr.abort();
JK 948         if(trams_timer) clearTimeout(trams_timer);
949         if(buses_xhr) buses_xhr.abort();
950         if(buses_timer) clearTimeout(buses_timer);
57b8d3 951           
JK 952         fail(lang.error_refresh);
953     }, 1800000);
954 }
955
956 init();