In addition to using const and let, this also upgrades our linter config and removes lint (mostly whitespace).
227 lines
6.0 KiB
JavaScript
227 lines
6.0 KiB
JavaScript
import Geolocation from '../src/ol/Geolocation.js';
|
|
import Map from '../src/ol/Map.js';
|
|
import Overlay from '../src/ol/Overlay.js';
|
|
import View from '../src/ol/View.js';
|
|
import {defaults as defaultControls} from '../src/ol/control.js';
|
|
import LineString from '../src/ol/geom/LineString.js';
|
|
import TileLayer from '../src/ol/layer/Tile.js';
|
|
import {fromLonLat} from '../src/ol/proj.js';
|
|
import OSM from '../src/ol/source/OSM.js';
|
|
|
|
// creating the view
|
|
const view = new View({
|
|
center: fromLonLat([5.8713, 45.6452]),
|
|
zoom: 19
|
|
});
|
|
|
|
// creating the map
|
|
const map = new Map({
|
|
layers: [
|
|
new TileLayer({
|
|
source: new OSM()
|
|
})
|
|
],
|
|
target: 'map',
|
|
controls: defaultControls({
|
|
attributionOptions: {
|
|
collapsible: false
|
|
}
|
|
}),
|
|
view: view
|
|
});
|
|
|
|
// Geolocation marker
|
|
const markerEl = document.getElementById('geolocation_marker');
|
|
const marker = new Overlay({
|
|
positioning: 'center-center',
|
|
element: markerEl,
|
|
stopEvent: false
|
|
});
|
|
map.addOverlay(marker);
|
|
|
|
// LineString to store the different geolocation positions. This LineString
|
|
// is time aware.
|
|
// The Z dimension is actually used to store the rotation (heading).
|
|
const positions = new LineString([],
|
|
/** @type {ol.geom.GeometryLayout} */ ('XYZM'));
|
|
|
|
// Geolocation Control
|
|
const geolocation = new Geolocation({
|
|
projection: view.getProjection(),
|
|
trackingOptions: {
|
|
maximumAge: 10000,
|
|
enableHighAccuracy: true,
|
|
timeout: 600000
|
|
}
|
|
});
|
|
|
|
let deltaMean = 500; // the geolocation sampling period mean in ms
|
|
|
|
// Listen to position changes
|
|
geolocation.on('change', function() {
|
|
const position = geolocation.getPosition();
|
|
const accuracy = geolocation.getAccuracy();
|
|
const heading = geolocation.getHeading() || 0;
|
|
const speed = geolocation.getSpeed() || 0;
|
|
const m = Date.now();
|
|
|
|
addPosition(position, heading, m, speed);
|
|
|
|
const coords = positions.getCoordinates();
|
|
const len = coords.length;
|
|
if (len >= 2) {
|
|
deltaMean = (coords[len - 1][3] - coords[0][3]) / (len - 1);
|
|
}
|
|
|
|
const html = [
|
|
'Position: ' + position[0].toFixed(2) + ', ' + position[1].toFixed(2),
|
|
'Accuracy: ' + accuracy,
|
|
'Heading: ' + Math.round(radToDeg(heading)) + '°',
|
|
'Speed: ' + (speed * 3.6).toFixed(1) + ' km/h',
|
|
'Delta: ' + Math.round(deltaMean) + 'ms'
|
|
].join('<br />');
|
|
document.getElementById('info').innerHTML = html;
|
|
});
|
|
|
|
geolocation.on('error', function() {
|
|
alert('geolocation error');
|
|
// FIXME we should remove the coordinates in positions
|
|
});
|
|
|
|
// convert radians to degrees
|
|
function radToDeg(rad) {
|
|
return rad * 360 / (Math.PI * 2);
|
|
}
|
|
// convert degrees to radians
|
|
function degToRad(deg) {
|
|
return deg * Math.PI * 2 / 360;
|
|
}
|
|
// modulo for negative values
|
|
function mod(n) {
|
|
return ((n % (2 * Math.PI)) + (2 * Math.PI)) % (2 * Math.PI);
|
|
}
|
|
|
|
function addPosition(position, heading, m, speed) {
|
|
const x = position[0];
|
|
const y = position[1];
|
|
const fCoords = positions.getCoordinates();
|
|
const previous = fCoords[fCoords.length - 1];
|
|
const prevHeading = previous && previous[2];
|
|
if (prevHeading) {
|
|
let headingDiff = heading - mod(prevHeading);
|
|
|
|
// force the rotation change to be less than 180°
|
|
if (Math.abs(headingDiff) > Math.PI) {
|
|
const sign = (headingDiff >= 0) ? 1 : -1;
|
|
headingDiff = -sign * (2 * Math.PI - Math.abs(headingDiff));
|
|
}
|
|
heading = prevHeading + headingDiff;
|
|
}
|
|
positions.appendCoordinate([x, y, heading, m]);
|
|
|
|
// only keep the 20 last coordinates
|
|
positions.setCoordinates(positions.getCoordinates().slice(-20));
|
|
|
|
// FIXME use speed instead
|
|
if (heading && speed) {
|
|
markerEl.src = 'data/geolocation_marker_heading.png';
|
|
} else {
|
|
markerEl.src = 'data/geolocation_marker.png';
|
|
}
|
|
}
|
|
|
|
// recenters the view by putting the given coordinates at 3/4 from the top or
|
|
// the screen
|
|
function getCenterWithHeading(position, rotation, resolution) {
|
|
const size = map.getSize();
|
|
const height = size[1];
|
|
|
|
return [
|
|
position[0] - Math.sin(rotation) * height * resolution * 1 / 4,
|
|
position[1] + Math.cos(rotation) * height * resolution * 1 / 4
|
|
];
|
|
}
|
|
|
|
let previousM = 0;
|
|
function updateView() {
|
|
// use sampling period to get a smooth transition
|
|
let m = Date.now() - deltaMean * 1.5;
|
|
m = Math.max(m, previousM);
|
|
previousM = m;
|
|
// interpolate position along positions LineString
|
|
const c = positions.getCoordinateAtM(m, true);
|
|
if (c) {
|
|
view.setCenter(getCenterWithHeading(c, -c[2], view.getResolution()));
|
|
view.setRotation(-c[2]);
|
|
marker.setPosition(c);
|
|
}
|
|
}
|
|
|
|
// geolocate device
|
|
const geolocateBtn = document.getElementById('geolocate');
|
|
geolocateBtn.addEventListener('click', function() {
|
|
geolocation.setTracking(true); // Start position tracking
|
|
|
|
map.on('postcompose', updateView);
|
|
map.render();
|
|
|
|
disableButtons();
|
|
}, false);
|
|
|
|
// simulate device move
|
|
let simulationData;
|
|
const client = new XMLHttpRequest();
|
|
client.open('GET', 'data/geolocation-orientation.json');
|
|
|
|
|
|
/**
|
|
* Handle data loading.
|
|
*/
|
|
client.onload = function() {
|
|
simulationData = JSON.parse(client.responseText).data;
|
|
};
|
|
client.send();
|
|
|
|
const simulateBtn = document.getElementById('simulate');
|
|
simulateBtn.addEventListener('click', function() {
|
|
const coordinates = simulationData;
|
|
|
|
const first = coordinates.shift();
|
|
simulatePositionChange(first);
|
|
|
|
let prevDate = first.timestamp;
|
|
function geolocate() {
|
|
const position = coordinates.shift();
|
|
if (!position) {
|
|
return;
|
|
}
|
|
const newDate = position.timestamp;
|
|
simulatePositionChange(position);
|
|
window.setTimeout(function() {
|
|
prevDate = newDate;
|
|
geolocate();
|
|
}, (newDate - prevDate) / 0.5);
|
|
}
|
|
geolocate();
|
|
|
|
map.on('postcompose', updateView);
|
|
map.render();
|
|
|
|
disableButtons();
|
|
}, false);
|
|
|
|
function simulatePositionChange(position) {
|
|
const coords = position.coords;
|
|
geolocation.set('accuracy', coords.accuracy);
|
|
geolocation.set('heading', degToRad(coords.heading));
|
|
const projectedPosition = fromLonLat([coords.longitude, coords.latitude]);
|
|
geolocation.set('position', projectedPosition);
|
|
geolocation.set('speed', coords.speed);
|
|
geolocation.changed();
|
|
}
|
|
|
|
function disableButtons() {
|
|
geolocateBtn.disabled = 'disabled';
|
|
simulateBtn.disabled = 'disabled';
|
|
}
|