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){ 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'; } }); })()