See this blog post for context and details.
xxxxxxxxxx
<html>
<head>
<meta charset=utf-8 />
<title>Le Zigomar !</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.5.1/dist/leaflet.css"
integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ=="
crossorigin=""/>
<script src="https://unpkg.com/leaflet@1.5.1/dist/leaflet.js"
integrity="sha512-GffPMF3RvMeYyc1LWMHtK8EbPv0iNZ8/oTtHPx9/cc2ILxQ+u905qIwdpULaqDkyBKgOaB57QTMg7ztg8Jm2Og=="
crossorigin=""></script>
<script src="https://calvinmetcalf.github.io/leaflet-ajax/dist/leaflet.ajax.js"></script>
<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
.info { padding: 6px 8px; font: 14px/16px Arial, Helvetica, sans-serif; background: rgba(255,255,255,0.8); box-shadow: 0 0 15px rgba(0,0,0,0.2); border-radius: 4px; }
.info p { margin-top: 0; }
.info #speed { background: #d7301f; height: 4px; width: 0; transition: all 0.5s ease; }
.glyphicon { display: inline-block; width: 16px; height: 16px; background-size: 16px; }
.glyphicon-globe { background-image: url(earth.svg); }
.glyphicon-target { background-image: url(target.svg); }
.glyphicon-play { background-image: url(play.svg); }
.glyphicon-pause { background-image: url(pause.svg); }
.glyphicon-replay { background-image: url(replay.svg); }
.glyphicon-time { background-image: url(time.svg); }
</style>
</head>
<body>
<div id="map"></div>
<script>
var bounds;
var map = L.map('map', {
center: [48.31356955685135, -4.523620605468751],
zoom: 10,
playing: false,
tracking: false
});
L.tileLayer('https://{s}.basemaps.cartocdn.com/rastertiles/voyager_labels_under/{z}/{x}/{y}{r}.png', {
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors © <a href="https://carto.com/attributions">CARTO</a>',
subdomains: 'abcd',
maxZoom: 19
}).addTo(map);
L.Util.ajax('2019_09_07.json').then(function(data){
var bounds = L.geoJSON(data).getBounds();
map.fitBounds(bounds);
var trackControl = L.control({position: 'topright'});
trackControl.onAdd = function(map) {
var container = L.DomUtil.create('div', 'leaflet-bar');
var animButton = L.DomUtil.create('a', 'glyphicon glyphicon-play', container);
animButton.setAttribute('role', 'button');
animButton.title = 'Start, pause or replay the animation';
animButton.setAttribute('aria-label', animButton.title);
animButton.setAttribute('id', 'animButton');
// usefull ?
L.DomEvent.on(animButton, 'click', L.DomEvent.stopPropagation);
L.DomEvent.on(animButton, 'click', L.DomEvent.preventDefault);
//
L.DomEvent.on(animButton, 'click', function() {
// Play
if (L.DomUtil.hasClass(animButton, 'glyphicon-play') === true) {
L.DomUtil.setClass(animButton, 'glyphicon glyphicon-pause');
map.playing = true;
window.requestAnimationFrame(addSegment);
}
// Pause
else if (L.DomUtil.hasClass(animButton, 'glyphicon-pause') === true) {
L.DomUtil.setClass(animButton, 'glyphicon glyphicon-play');
map.playing = false;
}
// Replay
else {
L.DomUtil.setClass(animButton, 'glyphicon glyphicon-pause');
map.playing = true;
i = 2;
window.requestAnimationFrame(addSegment);
}
}, this);
var trackButton = L.DomUtil.create('a', 'glyphicon glyphicon-target', container);
trackButton.setAttribute('role', 'button');
trackButton.title = 'Click to track the boats. Click again to zoom out to full view';
trackButton.setAttribute('aria-label', animButton.title);
// usefull ?
L.DomEvent.on(trackButton, 'click', L.DomEvent.stopPropagation);
L.DomEvent.on(trackButton, 'click', L.DomEvent.preventDefault);
//
L.DomEvent.on(trackButton, 'click', function() {
if (L.DomUtil.hasClass(trackButton, 'glyphicon-target') === true) {
L.DomUtil.setClass(trackButton, 'glyphicon glyphicon-globe');
map.setView(position._latlng, 15);
map.tracking = true;
} else {
L.DomUtil.setClass(trackButton, 'glyphicon glyphicon-target');
map.fitBounds(bounds);
map.tracking = false;
};
}, this);
return container;
}
trackControl.addTo(map);
var maxSpeed = Math.max.apply(Math, data.features.map(function(o) { return o.properties.speed; }));
var info = L.control({position: 'bottomright'});
info.onAdd = function (map) {
var div = L.DomUtil.create('div', 'info');
// https://stackoverflow.com/a/4020842 / find max value in array with js
var time = new Date(data.features[0].properties.time.replace(/\//g, '-').slice(0, -3)).toTimeString().split(' ')[0];
div.innerHTML = '<p id="chrono"><span class="glyphicon glyphicon-time" aria-hidden="true"></span> Time <span id="time">' + time +'</span></p><p>Speed (max ' + Math.round(maxSpeed * 1.94384 * 100) / 100 + ' knots)</p><p id="speed"></p>';
return div;
};
info.addTo(map);
var stylePoint = {
radius: 30,
fillColor: "#d7301f",
fillOpacity: 1,
color: "#d7301f"
};
var point_1 = new L.LatLng(data.features[0].geometry.coordinates[1], data.features[0].geometry.coordinates[0]);
var point_2 = new L.LatLng(data.features[1].geometry.coordinates[1], data.features[1].geometry.coordinates[0]);
var pointList = [point_1, point_2];
var trailLine = L.polyline(pointList, {color: '#d7301f', weight: 1.5}).addTo(map);
var trackLine = L.polyline(pointList, {color: '#d7301f', weight: 1, opacity: 0.2}).addTo(map);
var position = L.circle([data.features[0].geometry.coordinates[1], data.features[0].geometry.coordinates[0]], stylePoint).addTo(map);
var i = 2;
function draw(layer, polyline, position, splice) {
position._latlng.lat = layer.features[i].geometry.coordinates[1];
position._latlng.lng = layer.features[i].geometry.coordinates[0];
position.redraw();
var point_n = new L.LatLng(layer.features[i].geometry.coordinates[1], layer.features[i].geometry.coordinates[0]);
if (polyline._latlngs.length < 11) {
polyline.addLatLng(point_n);
}
else {
if (splice) {
polyline.getLatLngs().splice(0, 1);
}
polyline.addLatLng(point_n);
}
if (map.tracking === true) {
map.setView(position._latlng, 15);
}
}
function addSegment() {
var time = L.DomUtil.get('time');
var speed = L.DomUtil.get('speed');
if (i++ < data.features.length - 1) {
draw(data, trailLine, position, true);
draw(data, trackLine, position, false);
if (i%6==0) {
time.innerHTML = new Date(data.features[i].properties.time.replace(/\//g, '-').slice(0, -3)).toTimeString().split(' ')[0];
speed.setAttribute('style', 'width: ' + data.features[i].properties.speed * 100 / maxSpeed + '%');
}
} else {
map.playing = false;
L.DomUtil.setClass(L.DomUtil.get('animButton'), 'glyphicon glyphicon-replay');
trailLine.getLatLngs().splice(0, 11);
L.DomUtil.get('speed').setAttribute('style', 'width: 0');
}
if(map.playing){
window.requestAnimationFrame(addSegment);
}
}
});
</script>
</body>
</html>
https://calvinmetcalf.github.io/leaflet-ajax/dist/leaflet.ajax.js