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