From a4d011116c35bb9bef8589c88ed38b3043f9e072 Mon Sep 17 00:00:00 2001 From: Jacek Kowalski <Jacek@jacekk.info> Date: Sat, 09 Feb 2019 13:42:55 +0000 Subject: [PATCH] Add geolocation tracking feature --- map.js | 297 +++++++++++++++++++++++++++++++++---------------- lang_en.js | 2 map.html | 16 ++ lang_pl.js | 2 map.css | 32 +++++ 5 files changed, 247 insertions(+), 102 deletions(-) diff --git a/lang_en.js b/lang_en.js index 745a92e..2b7f004 100644 --- a/lang_en.js +++ b/lang_en.js @@ -50,6 +50,7 @@ type_stoppoint: 'Stop point', type_stoppoint_bus: 'Bus stop point', type_stoppoint_tram: 'Tram stop point', + type_location: 'Your location', select_feature: 'There is more than one feature here. Select the proper one:', departures_for_stop: 'Click for stop departures (and not only this stop point).', @@ -84,4 +85,5 @@ error_request_failed_connectivity: 'Request failed - please check your network connectivity.', error_new_version: 'Website has been updated, reloading...', error_refresh: 'Error! Refresh the page to update information.', + error_location: 'Location failed.', }; diff --git a/lang_pl.js b/lang_pl.js index c3f58cf..648c5c9 100644 --- a/lang_pl.js +++ b/lang_pl.js @@ -52,6 +52,7 @@ type_stoppoint: 'Peron', type_stoppoint_bus: 'Peron autobusowy', type_stoppoint_tram: 'Peron tramwajowy', + type_location: 'Twoja lokalizacja', select_feature: 'W tym miejscu znajduje się więcej niż jeden element. Wybierz właściwy z listy:', departures_for_stop: 'Kliknij, by zobaczyć odjazdy dla całego przystanku (a nie tylko peronu).', @@ -86,4 +87,5 @@ error_request_failed_connectivity: 'Wykonanie żądania internetowego nie udało się - sprawdź połączenie z siecią.', error_new_version: 'Strona została zaktualizowana, przeładowuję...', error_refresh: 'Błąd! Odśwież stronę, by zaktualizować dane.', + error_location: 'Lokalizacja nieudana.', }; diff --git a/map.css b/map.css index 56ca077..29178f4 100644 --- a/map.css +++ b/map.css @@ -52,10 +52,15 @@ } } -#popup .close { +.close { float: right; cursor: pointer; font-size: 20px; + padding: 0px 10px; +} +#fail .close { + font-size: inherit; + height: 1em; } #popup .type { padding-bottom: 0; @@ -125,6 +130,31 @@ .ol-zoom { top: 2.2em; } +#track { + top: 6em; + left: 0.5em; +} +.ol-touch #track { + top: 7em; +} + +.ol-control button svg * { + stroke: white; + stroke-width: 5%; + fill: none; +} +.ol-control button svg .fill { + fill: white; +} +.ol-control button.clicked { + background-color: black; +} +.ol-control button.clicked:hover, .ol-control button.clicked:active { + background-color: rgba(0,0,0,.5); +} +.ol-control button.hidden { + display: none; +} a { color: #337ab7; diff --git a/map.html b/map.html index 2126425..ff694f3 100644 --- a/map.html +++ b/map.html @@ -5,19 +5,27 @@ <meta charset="utf-8" /> <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width" /> <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v5.3.0/css/ol.css" integrity="sha384-C7SzZySesoxngSK5V0BaD1DUap0LPZGWZpnXQGoIwvBXFc8G21y4s1QYvyr84FNa" crossorigin="anonymous"> -<link rel="stylesheet" href="map.css" type="text/css" /> +<link rel="stylesheet" href="map.css?v2" type="text/css" /> </head> <body> <div id="map"> <div id="title" class="ol-unselectable ol-control"><a href="/">TTSS Kraków</a></div> -<div id="fail" class="ol-unselectable ol-control"></div> +<div id="track" class="ol-unselectable ol-control"><button type="button" title="GPS"><svg width="100%" height="100%"> +<circle cx="50%" cy="50%" r="25%"></circle> +<circle cx="50%" cy="50%" r="10%" class="fill"></circle> +<line x1="50%" y1="25%" x2="50%" y2="10%"></line> +<line x1="75%" y1="50%" x2="90%" y2="50%"></line> +<line x1="50%" y1="75%" x2="50%" y2="90%"></line> +<line x1="25%" y1="50%" x2="10%" y2="50%"></line> +</svg></button></div> +<div id="fail" class="ol-unselectable ol-control"><span></span> <span class="close">×</p></div> </div> <div id="popup"></div> <script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha384-tsQFqpEReu7ZLhBV2VZlAu7zcOV+rXbYlF2cqB8txI/8aZajjp4Bqd+V6D5IgvKT" crossorigin="anonymous"></script> <script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=requestAnimationFrame,Element.prototype.classList"></script> <script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v5.3.0/build/ol.js" integrity="sha384-iQkGyyH4ioz3m+maM3s9MX1Oq67mACa4B9Z3ovUv3Sv37LJ96fx3WnZfLoiC3Wfl" crossorigin="anonymous"></script> -<script tyle="text/javascript" src="lang_pl.js?v3" id="lang_script"></script> +<script tyle="text/javascript" src="lang_pl.js?v4" id="lang_script"></script> <script tyle="text/javascript" src="common.js?v6"></script> -<script tyle="text/javascript" src="map.js?v9"></script> +<script tyle="text/javascript" src="map.js?v10"></script> </body> </html> diff --git a/map.js b/map.js index 3705044..2db053a 100644 --- a/map.js +++ b/map.js @@ -3,6 +3,14 @@ var ttss_refresh = 10000; // 10 seconds var ttss_position_type = 'CORRECTED'; +var geolocation = null; +var geolocation_set = 0; +var geolocation_button = null; +var geolocation_feature = null; +var geolocation_accuracy = null; +var geolocation_source = null; +var geolocation_layer = null; + var trams_xhr = null; var trams_timer = null; var trams_last_update = 0; @@ -41,11 +49,12 @@ var popup_element = document.getElementById('popup'); var popup_close_callback; var fail_element = document.getElementById('fail'); +var fail_text = document.querySelector('#fail span'); var ignore_hashchange = false; function fail(msg) { - setText(fail_element, msg); + setText(fail_text, msg); fail_element.style.top = '0.5em'; } @@ -501,7 +510,14 @@ table.appendChild(thead); table.appendChild(tbody); + var tabular_data = true; + switch(feature.getId().substr(0, 1)) { + case 'l': + tabular_data = false; + type = ''; + name = lang.type_location; + break; case 't': case 'b': type = lang.type_tram; @@ -584,14 +600,16 @@ div.appendChild(additional); } - div.appendChild(table); + if(tabular_data) { + div.appendChild(table); + ignore_hashchange = true; + window.location.hash = '#!' + feature.getId(); + } setTimeout(function () {map.getView().animate({ center: feature.getGeometry().getCoordinates(), }) }, 10); - ignore_hashchange = true; - window.location.hash = '#!' + feature.getId(); showPanel(div, function() { if(!ignore_hashchange) { @@ -607,6 +625,127 @@ }); feature_clicked = feature; +} + +function mapClicked(e) { + var point = e.coordinate; + var features = []; + map.forEachFeatureAtPixel(e.pixel, function(feature, layer) { + if(layer == stop_selected_layer) return; + if(feature.getId()) features.push(feature); + }); + + if(features.length > 1) { + featureClicked(); + + var div = document.createElement('div'); + + addParaWithText(div, lang.select_feature); + + for(var i = 0; i < features.length; i++) { + var feature = features[i]; + + var p = document.createElement('p'); + var a = document.createElement('a'); + p.appendChild(a); + a.addEventListener('click', function(feature) { return function() { + featureClicked(feature); + }}(feature)); + + var type = ''; + switch(feature.getId().substr(0, 1)) { + case 'l': + type = ''; + name = lang.type_location; + break; + case 't': + case 'b': + type = lang.type_tram; + if(feature.getId().startsWith('b')) { + type = lang.type_bus; + } + if(feature.get('vehicle_type').num) { + type += ' ' + feature.get('vehicle_type').num; + } + break; + case 's': + type = lang.type_stop_tram; + if(feature.getId().startsWith('sb')) { + type = lang.type_stop_bus; + } + break; + case 'p': + type = lang.type_stoppoint_tram; + if(feature.getId().startsWith('pb')) { + type = lang.type_stoppoint_bus; + } + break; + } + + addElementWithText(a, 'span', type).className = 'small'; + a.appendChild(document.createTextNode(' ')); + addElementWithText(a, 'span', feature.get('name')); + + div.appendChild(p); + } + + showPanel(div); + + return; + } + + var feature = features[0]; + if(!feature) { + if(stops_buses_layer.getVisible()) { + feature = returnClosest(point, feature, stops_buses_source.getClosestFeatureToCoordinate(point)); + } + if(stops_trams_layer.getVisible()) { + feature = returnClosest(point, feature, stops_trams_source.getClosestFeatureToCoordinate(point)); + } + if(stop_points_buses_layer.getVisible()) { + feature = returnClosest(point, feature, stop_points_buses_source.getClosestFeatureToCoordinate(point)); + } + if(stop_points_trams_layer.getVisible()) { + feature = returnClosest(point, feature, stop_points_trams_source.getClosestFeatureToCoordinate(point)); + } + if(trams_layer.getVisible()) { + feature = returnClosest(point, feature, trams_source.getClosestFeatureToCoordinate(point)); + } + if(buses_layer.getVisible()) { + feature = returnClosest(point, feature, buses_source.getClosestFeatureToCoordinate(point)); + } + + if(getDistance(point, feature) > map.getView().getResolution() * 20) { + feature = null; + } + } + + featureClicked(feature); +} + +function trackingStop() { + geolocation_button.className = ""; + geolocation.setTracking(false); + + geolocation_source.clear(); +} +function trackingStart() { + geolocation_set = 0; + geolocation_button.className = "clicked"; + geolocation_feature.setGeometry(new ol.geom.Point(map.getView().getCenter())); + geolocation_accuracy.setGeometry(new ol.geom.Circle(map.getView().getCenter(), 100000)); + + geolocation_source.addFeature(geolocation_feature); + geolocation_source.addFeature(geolocation_accuracy); + + geolocation.setTracking(true); +} +function trackingToggle() { + if(geolocation.getTracking()) { + trackingStop(); + } else { + trackingStart(); + } } function hash() { @@ -785,6 +924,55 @@ ol.style.IconImageCache.shared.setSize(512); + geolocation_feature = new ol.Feature({ + name: '', + style: new ol.style.Style({ + image: new ol.style.Circle({ + fill: new ol.style.Fill({color: '#39C'}), + stroke: new ol.style.Stroke({color: '#FFF', width: 2}), + radius: 5, + }), + }), + }); + geolocation_feature.setId('location_point'); + geolocation_accuracy = new ol.Feature(); + geolocation_source = new ol.source.Vector({ + features: [], + }); + geolocation_layer = new ol.layer.Vector({ + source: geolocation_source, + }); + geolocation_button = document.querySelector('#track button'); + if(!navigator.geolocation) { + geolocation_button.className = 'hidden'; + } + + geolocation = new ol.Geolocation(); + geolocation.on('change:position', function() { + var coordinates = geolocation.getPosition(); + geolocation_feature.setGeometry(coordinates ? new ol.geom.Point(coordinates) : null); + if(geolocation_set < 1) { + geolocation_set = 1; + map.getView().animate({ + center: coordinates, + }) + } + }); + geolocation.on('change:accuracyGeometry', function() { + var accuracy = geolocation.getAccuracyGeometry(); + geolocation_accuracy.setGeometry(accuracy); + if(geolocation_set < 2) { + geolocation_set = 2; + map.getView().fit(accuracy); + } + }); + geolocation.on('error', function(error) { + fail(lang.error_location + ' ' + error.message); + trackingStop(); + geolocation_button.className = 'hidden'; + }); + geolocation_button.addEventListener('click', trackingToggle); + map = new ol.Map({ target: 'map', layers: [ @@ -792,6 +980,7 @@ source: new ol.source.OSM() }), route_layer, + geolocation_layer, stops_buses_layer, stops_trams_layer, stop_points_buses_layer, @@ -802,7 +991,8 @@ ], view: new ol.View({ center: ol.proj.fromLonLat([19.94, 50.06]), - zoom: 14 + zoom: 14, + maxZoom: 19, }), controls: ol.control.defaults({ attributionOptions: ({ @@ -814,103 +1004,16 @@ }), new ol.control.Control({ element: fail_element, - }) + }), + new ol.control.Control({ + element: document.getElementById('track'), + }), ]), loadTilesWhileAnimating: false, }); // Display popup on click - map.on('singleclick', function(e) { - var point = e.coordinate; - var features = []; - map.forEachFeatureAtPixel(e.pixel, function(feature, layer) { - if(layer == stop_selected_layer) return; - if(feature.getId()) features.push(feature); - }); - - if(features.length > 1) { - featureClicked(); - - var div = document.createElement('div'); - - addParaWithText(div, lang.select_feature); - - for(var i = 0; i < features.length; i++) { - var feature = features[i]; - - var p = document.createElement('p'); - var a = document.createElement('a'); - p.appendChild(a); - a.addEventListener('click', function(feature) { return function() { - featureClicked(feature); - }}(feature)); - - var type = ''; - switch(feature.getId().substr(0, 1)) { - case 't': - case 'b': - type = lang.type_tram; - if(feature.getId().startsWith('b')) { - type = lang.type_bus; - } - if(feature.get('vehicle_type').num) { - type += ' ' + feature.get('vehicle_type').num; - } - break; - case 's': - type = lang.type_stop_tram; - if(feature.getId().startsWith('sb')) { - type = lang.type_stop_bus; - } - break; - case 'p': - type = lang.type_stoppoint_tram; - if(feature.getId().startsWith('pb')) { - type = lang.type_stoppoint_bus; - } - break; - } - - addElementWithText(a, 'span', type).className = 'small'; - a.appendChild(document.createTextNode(' ')); - addElementWithText(a, 'span', feature.get('name')); - - div.appendChild(p); - } - - showPanel(div); - - return; - } - - var feature = features[0]; - if(!feature) { - if(stops_buses_layer.getVisible()) { - feature = returnClosest(point, feature, stops_buses_source.getClosestFeatureToCoordinate(point)); - } - if(stops_trams_layer.getVisible()) { - feature = returnClosest(point, feature, stops_trams_source.getClosestFeatureToCoordinate(point)); - } - if(stop_points_buses_layer.getVisible()) { - feature = returnClosest(point, feature, stop_points_buses_source.getClosestFeatureToCoordinate(point)); - } - if(stop_points_trams_layer.getVisible()) { - feature = returnClosest(point, feature, stop_points_trams_source.getClosestFeatureToCoordinate(point)); - } - if(trams_layer.getVisible()) { - feature = returnClosest(point, feature, trams_source.getClosestFeatureToCoordinate(point)); - } - if(buses_layer.getVisible()) { - feature = returnClosest(point, feature, buses_source.getClosestFeatureToCoordinate(point)); - } - - if(getDistance(point, feature) > map.getView().getResolution() * 20) { - feature = null; - } - } - - featureClicked(feature); - }); + map.on('singleclick', mapClicked); fail_element.addEventListener('click', function() { fail_element.style.top = '-10em'; -- Gitblit v1.9.1