496 lines
19 KiB
JavaScript
496 lines
19 KiB
JavaScript
var loader = new PIXI.Loader();
|
|
var colors = ['66FF00','FF5555','FFE419','2A7FFF','BC5FD3','C8AB37','ECECEC','FF6600'];
|
|
for (var i in colors){
|
|
loader.add('marker'+colors[i]+'', 'src/img/plane-'+colors[i]+'.png');
|
|
}
|
|
|
|
//global variable
|
|
var nbPlane = 0
|
|
|
|
loader.add('shadow', 'src/img/shadow.png');
|
|
loader.load(function(loader, resources) {
|
|
|
|
var pixiContainer = new PIXI.Container();
|
|
|
|
var map = L.map('map',{
|
|
zoomControl: false,
|
|
minZoom:2,
|
|
maxZoom: 16,
|
|
maxBounds: [
|
|
[-Infinity, -360],
|
|
[Infinity, 360]
|
|
],
|
|
//worldCopyJump: true,
|
|
attributionControl: false
|
|
})
|
|
|
|
//zoom controls
|
|
L.control.zoom({position: 'bottomright'}).addTo(map);
|
|
//Locate user
|
|
L.control.locate({position: 'bottomright'}).addTo(map);
|
|
//fullscreen
|
|
map.addControl(new L.Control.Fullscreen({position: 'bottomright'}));
|
|
|
|
// Licence Icon
|
|
L.Control.Watermark = L.Control.extend({
|
|
onAdd: function(map) {
|
|
var img = L.DomUtil.create('img');
|
|
|
|
img.src = 'src/img/licence.svg';
|
|
img.style.width = '100px';
|
|
|
|
return img;
|
|
}
|
|
});
|
|
L.control.watermark = function(opts) {
|
|
return new L.Control.Watermark(opts);
|
|
}
|
|
L.control.watermark({ position: 'bottomleft' }).addTo(map);
|
|
//scale
|
|
L.control.scale().addTo(map);
|
|
|
|
//disabling send plane button if alreada 100 planes sent the current day
|
|
|
|
|
|
$.post("avionJournalier.php", function(result) {
|
|
result = result.replace(/'/g, '"');
|
|
result = $.parseJSON(result)
|
|
nbPlane = Number(result.nAvion)
|
|
$("#buttonNewPlane").val("Lancer un avion-poème "+Number(result.nAvion)+"/100")
|
|
if (nbPlane > 99){
|
|
$("#buttonNewPlane").prop("disabled",true);
|
|
}
|
|
else{
|
|
|
|
}
|
|
translateUI(lang)
|
|
})
|
|
|
|
//background
|
|
L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
|
|
attribution: '',
|
|
//minZoom:8,
|
|
maxZoom: 16,
|
|
}).addTo(map);
|
|
|
|
//adding terminator
|
|
var terminator = L.terminator()
|
|
.addTo(map);
|
|
setInterval(function() {
|
|
terminator.setTime();
|
|
}, 1000);
|
|
|
|
|
|
//center map on sun by default
|
|
function centerOnSun(){
|
|
var equator = turf.lineString([[-1000, 0], [1000, 0]])
|
|
var intersects = turf.lineIntersect(equator, terminator.toGeoJSON());
|
|
//console.log(intersects)
|
|
var lonCenter = ((intersects.features[1].geometry.coordinates[0])+(intersects.features[2].geometry.coordinates[0]))/2
|
|
//console.log(lonCenter)
|
|
return lonCenter
|
|
}
|
|
map.setView([30,0], 2);
|
|
|
|
|
|
//could layer from GBIS NASA - always 1 day of delay
|
|
var twoDaysAgo = moment(new Date()).subtract(2, "days").format('YYYY-MM-DD')
|
|
L.tileLayer('https://map1.vis.earthdata.nasa.gov/wmts-webmerc/VIIRS_SNPP_CorrectedReflectance_TrueColor/default/'+twoDaysAgo+'/GoogleMapsCompatible_Level9/{z}/{y}/{x}.jpg', {
|
|
attribution: '',
|
|
maxZoom: 8,
|
|
opacity:0.4
|
|
})
|
|
.addTo(map);
|
|
|
|
|
|
//Bluring the terminator | Source: https://stackoverflow.com/questions/28235792/leaflet-polygon-with-fuzzy-outline
|
|
var svg = map.getPanes().overlayPane.firstChild,
|
|
svgFilter = document.createElementNS('http://www.w3.org/2000/svg', 'filter'),
|
|
svgBlur = document.createElementNS('http://www.w3.org/2000/svg', 'feGaussianBlur');
|
|
svgFilter.setAttribute('id', 'blur');
|
|
svgFilter.setAttribute('x', '-100%');
|
|
svgFilter.setAttribute('y', '-100%');
|
|
svgFilter.setAttribute('width', '500%');
|
|
svgFilter.setAttribute('height', '500%');
|
|
svgBlur.setAttribute('stdDeviation', 15);
|
|
svgFilter.appendChild(svgBlur);
|
|
svg.appendChild(svgFilter);
|
|
terminator._path.setAttribute('filter', 'url(#blur)');
|
|
|
|
//geojson for arclines
|
|
//travel
|
|
var arcTravelStyle = {
|
|
"color": "#00ff00",
|
|
"weight": 2.5,
|
|
"dashArray": "10 5",
|
|
"opacity": 0.65
|
|
};
|
|
var arcTravel = L.geoJSON([],{style: arcTravelStyle}).addTo(map);
|
|
//travelled
|
|
var arcTravelledStyle = {
|
|
"color": "#00ff00",
|
|
"weight": 2.5,
|
|
"opacity": 0.65
|
|
};
|
|
var arcTravelled = L.geoJSON([],{style: arcTravelledStyle}).addTo(map);
|
|
|
|
var pulsatingMarker = L.layerGroup([]).addTo(map)
|
|
var originMarker = L.layerGroup([]).addTo(map)
|
|
|
|
var firstDraw = true;
|
|
var prevZoom;
|
|
|
|
var pixiOverlay = L.pixiOverlay(function(utils, event) {
|
|
var zoom = utils.getMap().getZoom();
|
|
var container = utils.getContainer();
|
|
var renderer = utils.getRenderer();
|
|
var project = utils.latLngToLayerPoint;
|
|
var scale = utils.getScale();
|
|
|
|
if (firstDraw) {
|
|
|
|
//get planes
|
|
$.post( "getCurrentPlanes.php", function(result) {
|
|
// console.log(result)
|
|
var planes = $.parseJSON(result)
|
|
console.log(planes)
|
|
function formatResults(data){
|
|
data.deliveryTime = Number(data.deliveryTime)*1000
|
|
data.startTime = Number(data.startTime)*1000
|
|
return data
|
|
}
|
|
|
|
function displayPlane(data){
|
|
var line = turf.greatCircle(turf.point([Number(data.startLon),Number(data.startLat)]), turf.point([Number(data.destLon),Number(data.destLat)]),{offset:20})
|
|
var lineDistance = turf.length(line);
|
|
var dateStart = moment(data.startTime)
|
|
var dateEnd = moment(data.deliveryTime)
|
|
var dateNow = moment(new Date())
|
|
var totalSeconds = dateEnd.diff(dateStart,'seconds');
|
|
var travelledSeconds = dateNow.diff(dateStart,'seconds')
|
|
var travelRatio = travelledSeconds/totalSeconds
|
|
var travelledDistance = lineDistance*travelRatio // getting the travelled distance
|
|
var segment;
|
|
if(line.geometry.type == "MultiLineString"){ // if the arc is cutting the dateline, well it's a mess.
|
|
var l1 = turf.lineString(line.geometry.coordinates[0]) // creating a line from first segment
|
|
var l2 = turf.lineString(line.geometry.coordinates[1]) // creating a line from second segment
|
|
var l1Length = turf.length(l1) // calculating length of first segment
|
|
if (travelledDistance < l1Length){ // if the travelled distance is inferior to segment 1 line, the point is somewhere in it
|
|
segment = l1
|
|
data.position = 1
|
|
}
|
|
else{
|
|
segment = l2 // otherwise on segment 2
|
|
data.position = 2
|
|
travelledDistance = Number(travelledDistance)-Number(l1Length) // we remove the travel distance done in s1
|
|
}
|
|
}
|
|
else{
|
|
segment = line
|
|
data.position = 0
|
|
}
|
|
var currentPosition = turf.along(segment, travelledDistance,{units: 'kilometers'})
|
|
if(currentPosition.geometry.coordinates[0] < -180){currentPosition.geometry.coordinates[0] = currentPosition.geometry.coordinates[0]+360}
|
|
if(currentPosition.geometry.coordinates[0] > 180){currentPosition.geometry.coordinates[0] = currentPosition.geometry.coordinates[0]-Number(360)}
|
|
//calculating bearing based on two points, one just before and one just after
|
|
var positionBefore = turf.along(segment, (travelledDistance)-0.00001,{units: 'kilometers'})
|
|
var positionAfter = turf.along(segment, (travelledDistance)+0.00001,{units: 'kilometers'})
|
|
var rotation = turf.bearing(
|
|
positionBefore,
|
|
positionAfter
|
|
);
|
|
|
|
//plane
|
|
var iconColor = 'marker'+data.color
|
|
var markerTexture = resources[iconColor].texture;
|
|
var marker = new PIXI.Sprite(markerTexture);
|
|
marker.anchor.set(0.5, 0.5);
|
|
var markerCoords = project([currentPosition.geometry.coordinates[1],currentPosition.geometry.coordinates[0]]);
|
|
marker.x = markerCoords.x;
|
|
marker.y = markerCoords.y;
|
|
marker.angle = rotation;
|
|
marker.scale.set(5);
|
|
marker.interactive = true;
|
|
marker.cursor = 'pointer'
|
|
marker.data = data
|
|
marker.data.line = line
|
|
marker.data.currentPosition = currentPosition
|
|
|
|
//shadow
|
|
var shadowTexture = resources.shadow.texture;
|
|
var shadow = new PIXI.Sprite(shadowTexture);
|
|
shadow.anchor.set(0.5, 0.5);
|
|
shadow.x = markerCoords.x;
|
|
shadow.y = markerCoords.y + 0.5;
|
|
shadow.angle = rotation;
|
|
shadow.scale.set(0.00025);
|
|
shadow.alpha = 0.3
|
|
shadow.visible = false
|
|
|
|
//popup
|
|
marker.popup = L.popup({className: 'pixi-popup'})
|
|
|
|
pixiContainer.addChild(shadow);
|
|
pixiContainer.addChild(marker);
|
|
|
|
renderer.render(container)
|
|
|
|
//adapting scale based on zoom level. Max zoom = writting visible
|
|
map.on('zoomend', function() {
|
|
marker.scale.set((1/(utils.getScale(map.getZoom())*20))+(Math.pow(map.getZoom(),2)/100*2*(1/(utils.getScale(map.getZoom())*20))))
|
|
if(map.getZoom() == 16){
|
|
shadow.visible = true
|
|
}
|
|
else{
|
|
shadow.visible = false
|
|
}
|
|
});
|
|
|
|
|
|
|
|
//animating moving plane
|
|
setInterval(function() {
|
|
animate();
|
|
}, 1000)
|
|
|
|
function animate() {
|
|
if (map.getZoom() > 12){ // we animate only starting zoom 12
|
|
var bounds = map.getBounds()
|
|
var bbox = [bounds._southWest.lng, bounds._southWest.lat, bounds._northEast.lng, bounds._northEast.lat];
|
|
var extentPoly = turf.bboxPolygon(bbox);
|
|
var planeInExtent = turf.booleanWithin(marker.data.currentPosition, extentPoly);
|
|
if(planeInExtent == true){ // we animate only the planes in map extent
|
|
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 segment;
|
|
var travelledDistance = lineDistance*travelRatio // getting the travelled distance
|
|
if(line.geometry.type == "MultiLineString"){ // if the arc is cutting the dateline
|
|
var l1 = turf.lineString(line.geometry.coordinates[0]) // creating a line from first segment
|
|
var l2 = turf.lineString(line.geometry.coordinates[1]) // creating a line from second segment
|
|
var l1Length = turf.length(l1) // calculating length of first segment
|
|
if (travelledDistance < l1Length){ // if the travelled distance is inferior to segment 1 line, the point is somewhere in it
|
|
segment = l1
|
|
marker.data.position = 1 // we store it, usefull for popup event
|
|
}
|
|
else{
|
|
segment = l2 // otherwise on segment 2
|
|
marker.data.position = 2
|
|
travelledDistance = Number(travelledDistance)-Number(l1Length) // we remove the travel distance done in s1
|
|
}
|
|
}
|
|
else{
|
|
segment = line
|
|
}
|
|
var currentPosition = turf.along(segment, travelledDistance,{units: 'kilometers'})
|
|
//troubleshooting extreme lons
|
|
if(currentPosition.geometry.coordinates[0] < -180){currentPosition.geometry.coordinates[0] = currentPosition.geometry.coordinates[0]+360}
|
|
if(currentPosition.geometry.coordinates[0] > 180){currentPosition.geometry.coordinates[0] = currentPosition.geometry.coordinates[0]-Number(360)}
|
|
marker.data.currentPosition = currentPosition
|
|
var markerCoords = project([currentPosition.geometry.coordinates[1],currentPosition.geometry.coordinates[0]]);
|
|
marker.x = markerCoords.x;
|
|
marker.y = markerCoords.y;
|
|
shadow.x = markerCoords.x;
|
|
shadow.y = markerCoords.y + 0.5;
|
|
renderer.render(container);
|
|
}
|
|
}
|
|
}
|
|
|
|
//if parameter in URL, zoom on it
|
|
var queryString = window.location.search;
|
|
queryString = queryString.split('=')[1]
|
|
if(Number(queryString)==data.uid){
|
|
/*
|
|
Activate anim place reverse
|
|
$("#back").attr("src","src/img/back-"+data.color+".jpg");
|
|
html = data.message;
|
|
var canvas1 = document.getElementById("blankCanvas1");
|
|
rasterizeHTML.drawHTML(html, canvas1,{zoom:0.7}).then(function success(renderResult) {
|
|
var canvas2 = document.getElementById("blankCanvas2");
|
|
var ctx = canvas2.getContext("2d");
|
|
ctx.fillStyle = "#" + data.color;
|
|
ctx.fillRect(0,0,400,400);
|
|
ctx.drawImage(canvas1, 0, 0,400,400)
|
|
ctx.font = "9px Courier";
|
|
$("#front").attr('src',document.getElementById("blankCanvas2").toDataURL())
|
|
$("#canvas3d").css('display','block')
|
|
animePlane();
|
|
})
|
|
*/
|
|
}
|
|
|
|
}
|
|
for (var i in planes){
|
|
displayPlane(formatResults(planes[i]),0)
|
|
}
|
|
});
|
|
|
|
function openPopup(target){
|
|
//drawing arcs
|
|
arcTravel.clearLayers()
|
|
arcTravelled.clearLayers()
|
|
arcTravelStyle.color = '#'+target.data.color
|
|
arcTravelledStyle.color = '#'+target.data.color
|
|
var travel = target.data.line
|
|
var start = turf.point([Number(target.data.startLon),Number(target.data.startLat)])
|
|
var current = target.data.currentPosition
|
|
var end = turf.point([Number(target.data.destLon),Number(target.data.destLat)])
|
|
// for linestrings
|
|
if(travel.geometry.type == "LineString"){
|
|
var sliced1 = turf.lineSlice(start, current, travel);
|
|
var sliced2 = turf.lineSlice(current, end, travel);
|
|
arcTravelled.addData(sliced1);
|
|
arcTravel.addData(sliced2);
|
|
}
|
|
// we create a copy of lines crossing the datetime
|
|
function createCopy(c){
|
|
for (var i in c.geometry.coordinates){
|
|
if (c.geometry.coordinates[i][0] > 0){
|
|
c.geometry.coordinates[i][0] = c.geometry.coordinates[i][0] - Number(360)
|
|
}
|
|
else{
|
|
c.geometry.coordinates[i][0] = c.geometry.coordinates[i][0] + Number(360)
|
|
}
|
|
}
|
|
|
|
return c
|
|
}
|
|
//for multilinetrings
|
|
if(travel.geometry.type == "MultiLineString"){
|
|
var l1 = turf.lineString(travel.geometry.coordinates[0]) // creating a line from first segment
|
|
var l2 = turf.lineString(travel.geometry.coordinates[1]) // creating a line from second segment
|
|
if(target.data.position == 1){
|
|
var sliced1 = turf.lineSlice(start, current, l1);
|
|
var sliced2 = turf.lineSlice(current, turf.point(l1.geometry.coordinates[l1.geometry.coordinates.length-1]), l1);
|
|
arcTravelled.addData(sliced1);
|
|
arcTravel.addData(sliced2);
|
|
arcTravel.addData(l2);
|
|
|
|
}
|
|
else if(target.data.position == 2){
|
|
var sliced1 = turf.lineSlice(turf.point(l2.geometry.coordinates[0]), current, l2);
|
|
var sliced2 = turf.lineSlice(current, end, l2);
|
|
arcTravelled.addData(l1);
|
|
arcTravelled.addData(sliced1);
|
|
arcTravel.addData(sliced2);
|
|
}
|
|
var travelledGeoJSON = arcTravelled.toGeoJSON()
|
|
for (var i in travelledGeoJSON.features){
|
|
arcTravelled.addData(createCopy(travelledGeoJSON.features[i]))
|
|
}
|
|
var travelGeoJSON = arcTravel.toGeoJSON()
|
|
for (var i in travelGeoJSON.features){
|
|
arcTravel.addData(createCopy(travelGeoJSON.features[i]))
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//add pulsating marker on destination;
|
|
pulsatingMarker.clearLayers()
|
|
var pulseIcon = L.divIcon({
|
|
className: 'pulse-icon', //empty class to overwrite leaflet defaults
|
|
html: '<div class="pulse-icon-anim" style="border-color:#'+target.data.color+'"></div>',
|
|
iconSize: [22,22]
|
|
});
|
|
var endCoordo = [Number(target.data.destLat),Number(target.data.destLon)]
|
|
pulsatingMarker.addLayer(new L.marker(endCoordo, {icon: pulseIcon}));
|
|
if(travel.geometry.type == "MultiLineString"){ //if arc cross dateline, we multiply the marker
|
|
pulsatingMarker.addLayer(new L.marker([endCoordo[0],endCoordo[1]-Number(360)], {icon: pulseIcon}));
|
|
pulsatingMarker.addLayer(new L.marker([endCoordo[0],endCoordo[1]+Number(360)], {icon: pulseIcon}));
|
|
}
|
|
|
|
//adding static marker on origin
|
|
originMarker.clearLayers()
|
|
var startCoordo = [Number(target.data.startLat),Number(target.data.startLon)]
|
|
originMarker.addLayer(new L.circleMarker(startCoordo, {radius: 6,opacity:0.7, weight:2,color:'#'+target.data.color}));
|
|
if(travel.geometry.type == "MultiLineString"){ //if arc cross dateline, we multiply the marker
|
|
originMarker.addLayer(new L.circleMarker([startCoordo[0],startCoordo[1]-Number(360)], {radius: 6,opacity:0.7,weight:2,color:'#'+target.data.color}));
|
|
originMarker.addLayer(new L.circleMarker([startCoordo[0],startCoordo[1]+Number(360)], {radius: 6,opacity:0.7,color:'#'+target.data.color}));
|
|
}
|
|
|
|
|
|
|
|
//popup
|
|
var momentLocale = lang;
|
|
if (momentLocale == 'zh'){momentLocale = 'zh-cn'} //troubleshot for chinese
|
|
moment.locale(momentLocale)
|
|
|
|
target.popup.setLatLng([target.data.currentPosition.geometry.coordinates[1],target.data.currentPosition.geometry.coordinates[0]])
|
|
//arabic popup
|
|
if(lang=="ar"){
|
|
target.popup.setContent('<table><tbody><tr><th style="text-align:center">'+target.data.startName+'</th><th> <i class="fas fa-plane"></i> </th><th style="text-align:center">'+target.data.destName+'</th></tr><tr><th style="text-align:center"><small>'+moment(target.data.startTime).fromNow()+'</small></th><th></th><th style="text-align:center"><small>'+moment(target.data.deliveryTime).fromNow()+'</small></th></tr></tbody></table>')
|
|
}
|
|
//other languages
|
|
else{
|
|
target.popup.setContent('<table><tbody><tr><th style="text-align:center">'+target.data.startName+'</th><th> <i class="fas fa-plane"></i> </th><th style="text-align:center">'+target.data.destName+'</th></tr><tr><th style="text-align:center"><small>'+moment(target.data.startTime).fromNow()+'</small></th><th></th><th style="text-align:center"><small>'+moment(target.data.deliveryTime).fromNow()+'</small></th></tr></tbody></table>')
|
|
}
|
|
target.popup.openOn(map);
|
|
target.popup.on('remove', function() {
|
|
arcTravel.clearLayers()
|
|
arcTravelled.clearLayers()
|
|
pulsatingMarker.clearLayers()
|
|
originMarker.clearLayers()
|
|
});
|
|
target.popup.on('popupclose', function() {
|
|
arcTravel.clearLayers()
|
|
arcTravelled.clearLayers()
|
|
pulsatingMarker.clearLayers()
|
|
originMarker.clearLayers()
|
|
});
|
|
}
|
|
//utils for popup
|
|
utils.getMap().on('click', function(e) {
|
|
var interaction = utils.getRenderer().plugins.interaction;
|
|
var pointerEvent = e.originalEvent;
|
|
var pixiPoint = new PIXI.Point();
|
|
interaction.mapPositionToPoint(pixiPoint, pointerEvent.clientX, pointerEvent.clientY);
|
|
var target = interaction.hitTest(pixiPoint, container);
|
|
if (target && target.popup) {
|
|
openPopup(target)
|
|
}
|
|
});
|
|
utils.getMap().on('touchend', function(e) {
|
|
var interaction = utils.getRenderer().plugins.interaction;
|
|
var pointerEvent = e.originalEvent;
|
|
var pixiPoint = new PIXI.Point();
|
|
interaction.mapPositionToPoint(pixiPoint, pointerEvent.clientX, pointerEvent.clientY);
|
|
var target = interaction.hitTest(pixiPoint, container);
|
|
if (target && target.popup) {
|
|
openPopup(target)
|
|
}
|
|
});
|
|
|
|
|
|
}
|
|
|
|
firstDraw = false;
|
|
prevZoom = zoom;
|
|
renderer.render(container);
|
|
}, pixiContainer);
|
|
pixiOverlay.addTo(map);
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//hack to make white lines between lines disapear on non-retina screens (from: https://github.com/Leaflet/Leaflet/issues/3575 )
|
|
(function(){
|
|
var originalInitTile = L.GridLayer.prototype._initTile
|
|
L.GridLayer.include({
|
|
_initTile: function (tile) {
|
|
originalInitTile.call(this, tile);
|
|
|
|
var tileSize = this.getTileSize();
|
|
|
|
tile.style.width = tileSize.x + 1 + 'px';
|
|
tile.style.height = tileSize.y + 1 + 'px';
|
|
}
|
|
});
|
|
})()
|