mapboxgl.accessToken = config.mapboxKey;
const owmKey = config.owmKey
var map = new mapboxgl.Map({
container: 'map',
style: {
"glyphs": "mapbox://fonts/mapbox/{fontstack}/{range}.pbf",
"light": {
"anchor": "viewport",
"color": "white",
"intensity": 0.1
},
'version': 8,
'sources': {
'default-background': {
'type': 'raster',
'tiles': [
//'https://map1.vis.earthdata.nasa.gov/wmts-webmerc/VIIRS_CityLights_2012/default//GoogleMapsCompatible_Level8/{z}/{y}/{x}.jpg'
//'https://server.arcgisonline.com/ArcGIS/rest/services/World_Terrain_Base/MapServer/tile/{z}/{y}/{x}'
//'https://stamen-tiles-b.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.jpg'
'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}'
],
'tileSize': 256
}
},
'layers': [{
id: 'background',
type: 'background',
paint: {
'background-color': '#ffffff'
}
},
{
'id': 'default-background',
'type': 'raster',
'source': 'default-background',
'minzoom': 0,
'maxzoom': 18
}
]
},
center: [0, 20],
zoom: 2,
attributionControl: false
});
//prevent world duplication
map.setRenderWorldCopies(status === 'false')
map.on('load', function () {
//displaying clouds from OpenWeatherMap free API not enough
// map.addSource('clouds', {
// 'type': 'raster',
// 'tiles': [
// 'https://tile.openweathermap.org/map/clouds_new/{z}/{x}/{y}.png?appid='+owmKey+''
// ],
// 'tileSize': 256
// });
// map.addLayer({
// 'id': 'clouds',
// 'type': 'raster',
// 'source': 'clouds',
// 'minzoom': 0,
// 'maxzoom': 10,
// 'paint':{
// 'raster-opacity':0.5
// }
// });
//loading icons and colors, too lazy to make a loop, to change.
map.loadImage('src/img/plane-FFE419.png',
function (error, image) {
if (error) throw error;
map.addImage('plane-FFE419', image);
})
map.loadImage('src/img/plane-66FF00.png',
function (error, image) {
if (error) throw error;
map.addImage('plane-66FF00', image);
})
map.loadImage('src/img/plane-FF5555.png',
function (error, image) {
if (error) throw error;
map.addImage('plane-FF5555', image);
})
var colors = ["FFE419","66FF00","FF5555"]
//get now in GMT+0
var nowGMTSeconds = moment.tz(new Date(), "Europe/London").unix()
//get the planes from firestore
db.collection("planes").where("deliverySecondsServer", ">", nowGMTSeconds).get().then((querySnapshot) => { //change query to get only the ones who are still flying
querySnapshot.forEach((doc) => {
var id = doc.id
var data = doc.data()
//creating arcs
var line = turf.lineString([data.startCoordo,data.destCoordo])
var lineDistance = turf.length(line);
var arc = [];
var steps = 500;
for (var i = 0; i < lineDistance; i += lineDistance / steps) {
var segment = turf.along(line, i);
arc.push(segment.geometry.coordinates);
}
line.geometry.coordinates = arc;
//random color
var randomColor = colors[Math.floor(Math.random() * colors.length)];
map.addSource('route-'+id+'', {
'type': 'geojson',
'data': line
});
map.addLayer({
'id': 'route-'+id+'',
'source': 'route-'+id+'',
'type': 'line',
'paint': {
'line-width': 2,
'line-color': '#'+randomColor
}
});
map.setLayoutProperty('route-'+id+'', 'visibility', 'none');
//creating and animating planes
// calculating area already travelled
var dateStart = moment(data.sentDate[0])
var dateEnd = moment(data.deliveryDate[0])
var dateNow = moment(new Date())
var totalSeconds = dateEnd.diff(dateStart,'seconds');
var travelledSeconds = dateNow.diff(dateStart,'seconds')
var travelRatio = travelledSeconds/totalSeconds
var lineDistance = turf.length(line);
var currentPosition = turf.along(line, lineDistance*travelRatio,{units: 'kilometers'})
var plane = turf.point(currentPosition.geometry.coordinates,data)
//calculating bearing based on two points, one just before and one just after
var positionBefore = turf.along(line, (lineDistance*travelRatio)-0.00001,{units: 'kilometers'})
var positionAfter = turf.along(line, (lineDistance*travelRatio)+0.00001,{units: 'kilometers'})
plane.properties.bearing = turf.bearing(
positionBefore,
positionAfter
);
map.addSource('plane-'+id+'', {
'type': 'geojson',
'data': plane
});
map.addLayer({
'id': 'plane-'+id+'',
'source': 'plane-'+id+'',
'type': 'symbol',
'layout': {
'icon-image': 'plane-'+randomColor+'',
'icon-rotate': ['get', 'bearing'],
'icon-size':0.05,
'icon-rotation-alignment': 'map',
'icon-allow-overlap': true,
'icon-ignore-placement': true
}
});
//animating
function animate(){
var dateStart = moment(data.sentDate[0])
var dateEnd = moment(data.deliveryDate[0])
var dateNow = moment(new Date())
var totalSeconds = dateEnd.diff(dateStart,'seconds');
var travelledSeconds = dateNow.diff(dateStart,'seconds')
var travelRatio = travelledSeconds/totalSeconds
var lineDistance = turf.length(line);
var currentPosition = turf.along(line, lineDistance*travelRatio,{units: 'kilometers'})
var plane = turf.point(currentPosition.geometry.coordinates,data)
//calculating bearing based on two points, one just before and one just after
var positionBefore = turf.along(line, (lineDistance*travelRatio)-0.00001,{units: 'kilometers'})
var positionAfter = turf.along(line, (lineDistance*travelRatio)+0.00001,{units: 'kilometers'})
plane.properties.bearing = turf.bearing(
positionBefore,
positionAfter
);
map.getSource('plane-'+id+'').setData(plane);
var nowGMTSeconds = moment.tz(new Date(), "Europe/London").unix()
//removing the plane from map if arrived
if (data.deliverySecondsServer < nowGMTSeconds){
map.setLayoutProperty('plane-'+id+'', 'visibility', 'none');
}
requestAnimationFrame(animate)
}
// uncomment to animate below, still some clipping issues
//animate()
//on click on a plane: get popup + display route
map.on('click', 'plane-'+id+'', function (e) {
var coordinates = e.features[0].geometry.coordinates.slice();
var prop = e.features[0].properties;
var sentDate = JSON.parse(prop.sentDate)[0]
var deliveryDate = JSON.parse(prop.deliveryDate)[0]
while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) {
coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360;
}
//displying route
map.setLayoutProperty('route-'+id+'', 'visibility', 'visible');
var popup = new mapboxgl.Popup()
.setLngLat(coordinates)
.setHTML("Origine: "+prop.startName+"
"+sentDate+"
Destination: "+prop.destName+"
"+deliveryDate+"")
.addTo(map);
//removing route
popup.on('close', function(){
map.setLayoutProperty('route-'+id+'', 'visibility', 'none');
});
});
// Change the cursor to a pointer when the mouse is over the places layer.
map.on('mouseenter', 'plane-'+id+'', function () {
map.getCanvas().style.cursor = 'pointer';
});
// Change it back to a pointer when it leaves.
map.on('mouseleave', 'plane-'+id+'', function () {
map.getCanvas().style.cursor = '';
});
//adapt icon size to zoom level
map.on('zoom', function() {
map.setLayoutProperty('plane-'+id+'', 'icon-size', (map.getZoom())*0.025);
});
//URL Parameter : zooming to ID and opening popup when url in format avion-poe.me?plane=id
var queryString = window.location.search;
queryString = queryString.substring(7)
if(id == queryString){
map.setLayoutProperty('route-'+id+'', 'visibility', 'visible');
var bboxTravel = turf.bbox(line);
var bounds = [[bboxTravel[0],bboxTravel[1]],[bboxTravel[2],bboxTravel[3]]]
map.fitBounds(bounds, {
padding: 65,
speed: 0.5, // make the flying slow
curve: 0.8, // change the speed at which it zooms out
// This can be any easing function: it takes a number between
// 0 and 1 and returns another number between 0 and 1.
easing: function(
t) {
return t;
},
});
var sentDate = plane.properties.sentDate[0]
var deliveryDate = plane.properties.deliveryDate[0]
var popup = new mapboxgl.Popup()
.setLngLat(currentPosition.geometry.coordinates)
.setHTML("Origine: "+plane.properties.startName+"
"+sentDate+"
Destination: "+plane.properties.destName+"
"+deliveryDate+"")
.addTo(map);
//removing route
popup.on('close', function(){
map.setLayoutProperty('route-'+id+'', 'visibility', 'none');
});
}
});
});
})