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"); $(this).css("display", "none"); }); $("#downloadPlaneMessage").on("click", function () { const htmlRender = "
" + "
Métadonnées du message :
" + "
" + "
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"; }, }); })();