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) {
$("#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("opacity", 0);
$("#canvas3d").css("display", "block");
animePlane();
setTimeout(function () {
$("#canvas3d").css("opacity", 1);
}, 0);
const startDate = new Date(data.startTime);
const deliveryDate = new Date(data.deliveryTime);
const diffTime = Math.abs(deliveryDate - startDate);
const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
setTimeout(function () {
$("#closePlane").css("opacity", 1);
$("#closePlane").css("visibility", "visible");
$("#downloadPlaneMessage").css("opacity", 1);
$("#downloadPlaneMessage").css("visibility", "visible");
$("#closePlane").on("click", function () {
$("#canvas3d").css("display", "none");
$("#downloadPlaneMessage").css("display", "none");
$(this).css("display", "none");
});
$("#downloadPlaneMessage").on("click", function () {
const htmlRender =
"
" +
"
Métadonnées du message :
" +
"
" +
"- " +
data.startName +
" - " +
data.destName +
"
" +
"- " +
data.expeMail +
" - " +
data.destMail +
"
" +
"- " +
diffDays +
" days" +
"
" +
"- " +
moment(deliveryDate).format("DD/MM/YYYY") +
"
" +
"
" +
"
Champs message :
" +
html +
"
";
const opt = {
margin: 10,
filename: "peome.pdf",
image: { type: "jpeg", quality: 1 },
html2canvas: {
scale: 2,
removeContainer: false,
logging: true,
},
pagebreak: { avoid: "img" },
};
html2pdf().set(opt).from(htmlRender).save();
});
}, 5000);
});
setTimeout(function () {
map.setView(
[
data.currentPosition.geometry.coordinates[1],
data.currentPosition.geometry.coordinates[0],
],
15
);
openPopup(marker);
}, 100);
}
}
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:
'',
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(
'' +
target.data.startName +
' | | ' +
target.data.destName +
' |
---|
' +
moment(target.data.startTime).fromNow() +
' | | ' +
moment(target.data.deliveryTime).fromNow() +
" |
---|
"
);
}
//other languages
else {
target.popup.setContent(
'' +
target.data.startName +
' | | ' +
target.data.destName +
' |
---|
' +
moment(target.data.startTime).fromNow() +
' | | ' +
moment(target.data.deliveryTime).fromNow() +
" |
---|
"
);
}
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";
},
});
})();