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