Improved www.ttss.krakow.pl
Jacek Kowalski
2017-04-18 ca06643ed5544400aaa59a32309ab0455a25d761
commit | author | age
57b8d3 1 //var ttss_base = 'http://www.ttss.krakow.pl/internetservice';
JK 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
17 var map = null;
18 var popup_feature_id = null;
19 var popup_element = document.getElementById('popup');
20 var popup = null;
21 var fail_element = document.getElementById('fail');
22
7ca6a1 23 var ignore_hashchange = false;
JK 24
57b8d3 25 function fail(msg) {
JK 26     console.log(msg);
27     
28     setText(fail_element, msg);
29     fail_element.style.top = '0.5em';
30 }
31
32 function fail_ajax(data) {
33     // abort() is not a failure
34     if(data.readyState == 0 && data.statusText == 'abort') return;
35     
36     if(data.status == 0) {
37         fail(lang.error_request_failed_connectivity, data);
38     } else if (data.statusText) {
39         fail(lang.error_request_failed_status.replace('$status', data.statusText), data);
40     } else {
41         fail(lang.error_request_failed, data);
42     }
43 }
44
45 function popupHide() {
46     popup.setPosition(undefined);
47     popup_feature_id = null;
48 }
49
50 function popupShow(coordinates, id) {
51     popup.setPosition(coordinates);
52     if(id) {
53         popup_feature_id = id;
54     }
55 }
56
57 function getGeometry(object) {
58     return new ol.geom.Point(ol.proj.fromLonLat([object.longitude / 3600000.0, object.latitude / 3600000.0]))
59 }
60
61 function updateVehicles() {
62     if(vehicles_timer) clearTimeout(vehicles_timer);
63     if(vehicles_xhr) vehicles_xhr.abort();
64     
65     vehicles_xhr = $.get(
66         ttss_base + '/geoserviceDispatcher/services/vehicleinfo/vehicles' 
67             + '?positionType=CORRECTED'
68             + '&colorType=ROUTE_BASED'
69             + '&lastUpdate=' + encodeURIComponent(vehicles_last_update)
70     ).done(function(data) {
71         vehicles_last_update = data.lastUpdate;
72         
73         for(var i = 0; i < data.vehicles.length; i++) {
74             var vehicle = data.vehicles[i];
75             
76             var vehicle_feature = vehicles_source.getFeatureById('v' + vehicle.id);
77             if(vehicle.isDeleted) {
78                 if(vehicle_feature) {
79                     vehicles_source.removeFeature(vehicle_feature);
80                     if(popup_feature_id == vehicle_feature.getId()) {
81                         popupHide();
82                     }
83                 }
84                 continue;
85             }
86             
87             var vehicle_name_space = vehicle.name.indexOf(' ');
88             vehicle.line = vehicle.name.substr(0, vehicle_name_space);
89             vehicle.direction = vehicle.name.substr(vehicle_name_space+1);
90             if(special_directions[vehicle.direction]) {
91                 vehicle.line = special_directions[vehicle.direction];
92             }
93             
94             vehicle.geometry = getGeometry(vehicle);
95             vehicle.vehicle_type = parseVehicle(vehicle.id);
96             
97             if(!vehicle_feature) {
98                 vehicle_feature = new ol.Feature(vehicle);
99                 vehicle_feature.setId('v' + vehicle.id);
100                 
101                 var color_type = 'black';
102                 if(vehicle.vehicle_type) {
103                     switch(vehicle.vehicle_type.low) {
104                         case 0:
105                             color_type = 'orange';
106                             break;
107                         case 1:
108                             color_type = 'blue';
109                             break;
110                         case 2:
111                             color_type = 'green';
112                             break;
113                     }
114                 }
115                 
116                 vehicle_feature.setStyle(new ol.style.Style({
117                     image: new ol.style.RegularShape({
118                         fill: new ol.style.Fill({color: '#3399ff'}),
119                         stroke: new ol.style.Stroke({color: color_type, width: 2}),
120                         points: 3,
121                         radius: 12,
122                         rotation: Math.PI * parseFloat(vehicle.heading) / 180.0,
123                         rotateWithView: true,
124                         angle: 0
125                     }),
126                     text: new ol.style.Text({
127                         font: 'bold 10px sans-serif',
128                         text: vehicle.line,
129                         fill: new ol.style.Fill({color: 'white'}),
130                     }),
131                 }));
132                 vehicles_source.addFeature(vehicle_feature);
133             } else {
134                 vehicle_feature.setProperties(vehicle);
135                 vehicle_feature.getStyle().getImage().setRotation(Math.PI * parseFloat(vehicle.heading) / 180.0);
136                 
137                 if(popup_feature_id == vehicle_feature.getId()) {
138                     popupShow(vehicle_feature.getGeometry().getCoordinates());
139                 }
140             }
141         }
142         
143         vehicles_timer = setTimeout(function() {
144             updateVehicles();
145         }, ttss_refresh);
146     }).fail(fail_ajax);
7ca6a1 147     
JK 148     return vehicles_xhr;
57b8d3 149 }
JK 150
151 function updateStopSource(stops, prefix, source) {
152     source.clear();
153     
154     for(var i = 0; i < stops.length; i++) {
155         var stop = stops[i];
e61357 156         
JK 157         if(stop.category == 'other') continue;
158         
57b8d3 159         stop.geometry = getGeometry(stop);
JK 160         var stop_feature = new ol.Feature(stop);
161         
162         stop_feature.setId(prefix + stop.id);
163         stop_feature.setStyle(new ol.style.Style({
164             image: new ol.style.Circle({
165                 fill: new ol.style.Fill({color: 'orange'}),
166                 stroke: new ol.style.Stroke({color: 'red', width: 1}),
167                 radius: 3,
168             }),
169         }));
170         
171         source.addFeature(stop_feature);
172     }
173 }
174
175 function updateStops() {
7ca6a1 176     return $.get(
57b8d3 177         ttss_base + '/geoserviceDispatcher/services/stopinfo/stops'
JK 178             + '?left=-648000000'
179             + '&bottom=-324000000'
180             + '&right=648000000'
181             + '&top=324000000'
182     ).done(function(data) {
183         updateStopSource(data.stops, 's', stops_source);
184     }).fail(fail_ajax);
185 }
186
187 function updateStopPoints() {
7ca6a1 188     return $.get(
57b8d3 189         ttss_base + '/geoserviceDispatcher/services/stopinfo/stopPoints'
JK 190             + '?left=-648000000'
191             + '&bottom=-324000000'
192             + '&right=648000000'
193             + '&top=324000000'
194     ).done(function(data) {
195         updateStopSource(data.stopPoints, 'p', stop_points_source);
196     }).fail(fail_ajax);
7ca6a1 197 }
JK 198
199 function featureClicked(feature) {
200     if(!feature) {
201         popupHide();
202         
203         ignore_hashchange = true;
204         window.location.hash = '';
205         
206         return;
207     }
208     
209     var coordinates = feature.getGeometry().getCoordinates();
210     
211     deleteChildren(popup_element);
212     
213     addParaWithText(popup_element, feature.get('name')).className = 'bold';
214     switch(feature.getId().substr(0, 1)) {
215         case 'v':
216             var vehicle_type = parseVehicle(feature.get('id'));
217             if(vehicle_type) {
218                 addParaWithText(popup_element, vehicle_type.num + ' ' + vehicle_type.type);
219             }
220         break;
221     }
222     
223     ignore_hashchange = true;
224     window.location.hash = '#!' + feature.getId();
225     
226     popupShow(coordinates, feature.getId());
227 }
228
229 function hash() {
230     if(ignore_hashchange) {
231         ignore_hashchange = false;
232         return;
233     }
234     
235     var tramId = null;
236     
237     var vehicleId = null;
238     var stopId = null;
239     var stopPointId = null;
240     
241     var feature = null;
242     
243     if(window.location.hash.match(/^#!t[0-9]{3}$/)) {
244         tramId = parseInt(window.location.hash.substr(3));
245     } else if(window.location.hash.match(/^#![A-Za-z]{2}[0-9]{3}$/)) {
246         tramId = parseInt(window.location.hash.substr(4));
247     } else if(window.location.hash.match(/^#!v[0-9]+$/)) {
248         vehicleId = window.location.hash.substr(3);
249     } else if(window.location.hash.match(/^#!s[0-9]+$/)) {
250         stopId = window.location.hash.substr(3);
251     } else if(window.location.hash.match(/^#!p[0-9]+$/)) {
252         stopPointId = window.location.hash.substr(3);
253     }
254     
255     if(tramId) {
256         vehicleId = tramIdToVehicleId(tramId);
257     }
258     
259     if(vehicleId) {
260         feature = vehicles_source.getFeatureById('v' + vehicleId);
261     } else if(stopId) {
262         feature = stops_source.getFeatureById('s' + stopId);
263     } else if(stopPointId) {
264         feature = stop_points_source.getFeatureById('p' + stopPointId);
265     }
266     
267     featureClicked(feature);
268     if(feature) {
269         map.getView().setCenter(feature.getGeometry().getCoordinates());
270     }
57b8d3 271 }
JK 272
273 function init() {
274     if(!window.jQuery) {
275         fail(lang.jquery_not_loaded);
276         return;
277     }
278     
279     $.ajaxSetup({
280         dataType: 'json',
281         timeout: 10000,
282     });
283     
284     stops_source = new ol.source.Vector({
285         features: [],
286     });
287     stops_layer = new ol.layer.Vector({
288         source: stops_source,
289     });
290     
291     stop_points_source = new ol.source.Vector({
292         features: [],
293     });
294     stop_points_layer = new ol.layer.Vector({
295         source: stop_points_source,
296         visible: false,
297     });
298     
299     vehicles_source = new ol.source.Vector({
300         features: [],
301     });
302     vehicles_layer = new ol.layer.Vector({
303         source: vehicles_source,
304     });
305     
306     popup = new ol.Overlay({
307         element: popup_element,
308         positioning: 'bottom-center',
309         stopEvent: false,
310         offset: [0, -12]
311     });
312     
313     map = new ol.Map({
314         target: 'map',
315         layers: [
316             new ol.layer.Tile({
317                 source: new ol.source.OSM()
318             }),
319             stops_layer,
320             stop_points_layer,
321             vehicles_layer,
322         ],
323         overlays: [popup],
324         view: new ol.View({
325             center: ol.proj.fromLonLat([19.94, 50.06]),
326             zoom: 13
327         }),
328         controls: ol.control.defaults({
329             attributionOptions: ({
330                 collapsible: false,
331             })
332         }).extend([
333             new ol.control.Control({
334                 element: document.getElementById('title'),
335             }),
336             new ol.control.Control({
337                 element: fail_element,
338             })
339         ]),
340     });
341     
342     // Display popup on click
343     map.on('singleclick', function(e) {
344         var feature = map.forEachFeatureAtPixel(e.pixel, function(feature) { return feature; });
7ca6a1 345         featureClicked(feature);
57b8d3 346     });
JK 347
348     // Change mouse cursor when over marker
349     map.on('pointermove', function(e) {
350         var hit = map.hasFeatureAtPixel(e.pixel);
351         var target = map.getTargetElement();
352         target.style.cursor = hit ? 'pointer' : '';
353     });
354     
355     // Change layer visibility on zoom
356     map.getView().on('change:resolution', function(e) {
357         if(map.getView().getZoom() >= 16) {
358             stops_layer.setVisible(false);
359             stop_points_layer.setVisible(true);
360         } else {
361             stops_layer.setVisible(true);
362             stop_points_layer.setVisible(false);
363         }
364     });
365     
7ca6a1 366     $.when(
JK 367         updateVehicles(),
368         updateStops(),
369         updateStopPoints()
370     ).done(function() {
371         hash();
372     });
373     
374     window.addEventListener('hashchange', hash);
57b8d3 375     
JK 376     setTimeout(function() {
377         if(vehicles_xhr) vehicles_xhr.abort();
378         if(vehicles_timer) clearTimeout(vehicles_timer);
379           
380         fail(lang.error_refresh);
381     }, 1800000);
382 }
383
384 init();