1
0

js first commit

This commit is contained in:
leomartine 2021-03-19 14:01:44 +00:00
parent 63de1dd74a
commit 46d23393e3
5 changed files with 521 additions and 0 deletions

73
src/app.css Normal file
View File

@ -0,0 +1,73 @@
body {
margin: 0;
padding: 0;
font-family: 'PT Mono', monospace;
/* font-family: 'Special Elite', cursive !important */
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
.footer {
position: absolute;
bottom: 0;
width: 100%;
/* Set the fixed height of the footer here */
height: 30px;
line-height: 30px; /* Vertically center the text there */
background-color: #f5f5f5;
font-size: 12px
}
/* removing footer on mobile */
@media screen and (max-width: 400px) {
.footer {
display:none
}
}
/* big modal */
.modal-lg {
max-width:1140px !important;
}
.mapboxgl-map{
font-family: 'PT Mono', monospace;
/* font-family: 'Special Elite', cursive !important */
}
.mapboxgl-ctrl-logo{
display: none !important;
}
/* paper plane animation */
#canvas3d{
display:none;
transition: all .3s;
position:fixed;
height:100vh;
width:100vw;
background-color: rgba(0,0,0,0);
z-index:9999;
margin-top: 50vh; /* poussé de la moitié de hauteur de viewport */
transform: translateY(-50%) translateX(-50%); /* tiré de la moitié de sa propre hauteur */
margin-left: 50vw; /* poussé de la moitié de hauteur de viewport */
}
/* qill text editor */
#messageTextArea {
min-height: 200px;
border-radius: 0px 0px .25em .25em !important;
border-color: #ced4da !important
}
.ql-toolbar{
border-radius: .25em .25em 0px 0px !important;
border-color: #ced4da !important
}

4
src/config.js Normal file
View File

@ -0,0 +1,4 @@
var config = {
mapboxKey: 'pk.eyJ1IjoibGVvLWlnYSIsImEiOiJjazY2bGV3MTYxMjV3M25sMmdtNWluM2wzIn0.2THSqD6nz9OhE0Xsjnbw1g',
owmKey: "39d5c3be709ba4f5b6230cdffdedd2ac",
}

67
src/geocoder.js Normal file
View File

@ -0,0 +1,67 @@
let langGeocoder = "fr"
// Expediteur
var expeLocList = []
var expeLoc;
$('#expeGeocoderPhoton').typeahead({
source: function (query, process) {
expeLocList = []
var url = "https://photon.komoot.io/api?q="+query+"&lang="+langGeocoder+"&limit=4&osm_tag=place:city&osm_tag=place:town&osm_tag=place:village&osm_tag=place:municipality";
var res = []
return $.getJSON(url,function(data){
for (var i in data.features){
res.push(data.features[i].properties.name+" ("+data.features[i].properties.state+", "+data.features[i].properties.country+")")
expeLocList.push({
name: data.features[i].properties.name,
center: data.features[i].geometry.coordinates,
fullname: data.features[i].properties.name+" ("+data.features[i].properties.state+", "+data.features[i].properties.country+")"
})
}
return process(res)
})
},
matcher: function(item){ //needed when spelling not exact
return true
},
afterSelect: function (obj) {
for (var i in expeLocList){
if (expeLocList[i].fullname == obj){
expeLoc = expeLocList[i]
}
}
}
})
//Destinataire
var destLocList = []
var destLoc;
$('#destGeocoderPhoton').typeahead({
source: function (query, process) {
destLocList = []
var url = "https://photon.komoot.io/api?q="+query+"&lang="+langGeocoder+"&limit=4&osm_tag=place:city&osm_tag=place:town&osm_tag=place:village&osm_tag=place:municipality";
var res = []
return $.getJSON(url,function(data){
for (var i in data.features){
res.push(data.features[i].properties.name+" ("+data.features[i].properties.state+", "+data.features[i].properties.country+")")
destLocList.push({
name: data.features[i].properties.name,
center: data.features[i].geometry.coordinates,
fullname: data.features[i].properties.name+" ("+data.features[i].properties.state+", "+data.features[i].properties.country+")"
})
}
return process(res)
})
},
matcher: function(item){ //needed when spelling not exact
return true
},
afterSelect: function (obj) {
for (var i in destLocList){
if (destLocList[i].fullname == obj){
destLoc = destLocList[i]
}
}
}
})

266
src/map.js Normal file
View File

@ -0,0 +1,266 @@
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("<b>Origine:</b> "+prop.startName+"<br><em>"+sentDate+"</em><br><b>Destination:</b> "+prop.destName+"<br><em>"+deliveryDate+"</em>")
.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("<b>Origine:</b> "+plane.properties.startName+"<br><em>"+sentDate+"</em><br><b>Destination:</b> "+plane.properties.destName+"<br><em>"+deliveryDate+"</em>")
.addTo(map);
//removing route
popup.on('close', function(){
map.setLayoutProperty('route-'+id+'', 'visibility', 'none');
});
}
});
});
})

111
src/newPlane.js Normal file
View File

@ -0,0 +1,111 @@
// init text editor in modal
var quill = new Quill('#messageTextArea', {
modules: {
toolbar: [
[{ 'size': ['small', false, 'large'] }],
[{ 'font': [] }],
['bold', 'italic', 'underline','strike'],
[{ 'color': [] }],
]
},
placeholder: '',
theme: 'snow' // or 'bubble',
});
$("#sendNewPlane").on('click',function(){
//hiding navbar
$('.navbar-collapse').collapse('hide');
//hiding modal
$("#newPlaneModal").modal('hide')
var line = turf.lineString([expeLoc.center,destLoc.center]) //create line between start and end
var length = turf.length(line) //get length in Km
var speed = 30 //speed in km/h
var time = length/speed //time to delivery in hours
//get the local time with TZ for starting point
$.getJSON('https://api.mapbox.com/v4/examples.4ze9z6tv/tilequery/'+expeLoc.center[0]+','+expeLoc.center[1]+'.json?access_token='+mapboxgl.accessToken+'',function(startTime){
var expeTimezone = startTime.features[0].properties.TZID;
var sentDate = moment.tz(new Date(), expeTimezone);
sentDate = [sentDate.format(),expeTimezone]
//get the local time with TZ for end point
$.getJSON('https://api.mapbox.com/v4/examples.4ze9z6tv/tilequery/'+destLoc.center[0]+','+destLoc.center[1]+'.json?access_token='+mapboxgl.accessToken+'',function(endTime){
var destTimezone = endTime.features[0].properties.TZID;
var endDate = moment.tz(new Date(), destTimezone);
var deliveryDate = endDate.add(time, 'hours') // adding hours
deliveryDate = [endDate.format(),destTimezone]
//storing the endDate in GMT+0 in seconds for the query
var deliverySecondsServer = moment.tz(new Date(), "Europe/London").add(time, 'hours').unix()
var publicMessage = $("#publicMessage").prop("checked")
var message = $("#messageTextArea").val()
var data = {
'message':message,
'public':publicMessage,
'expeMail':$("#expeMail").val(),
'destMail':$("#destMail").val(),
'expeName':expeLoc.name,
'destName':destLoc.name,
'expeCoordo':expeLoc.center,
'destCoordo':destLoc.center,
'sentDate': sentDate,
'deliveryDate':deliveryDate,
'deliverySecondsServer':deliverySecondsServer,
}
//sending confirm mail
//sending notif mail
//scheduling mail
//adding the plane
var planes = db.collection('planes')
if (!publicMessage){ //removing message from DB if not public
data.message = ''
}
planes.add({
public: data.public,
message:data.message,
destName: data.destName,
destCoordo: data.destCoordo,
startName: data.expeName,
startCoordo: data.expeCoordo,
sentDate: data.sentDate,
deliveryDate: data.deliveryDate,
deliverySecondsServer: data.deliverySecondsServer
});
// creating image for plane, need to link it to the inputs tog et entered values.
var canvas = document.getElementById("blankCanvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "rgb(255,255,255)";
ctx.fillRect(0,0,400,400);
ctx.fillStyle = "rgb(0,0,0)";
ctx.font = "9px Courier";
ctx.fillText("Message: .- / -- --- -. / ... . -. ... --..-- /",10,60);
ctx.fillText("Expediteur: leo.martine@yahoo.fr - Annemasse",10,300);
ctx.fillText("Destinataire: leo.martine@yahoo.fr - Lyon",10,320);
$("#front").attr('src',document.getElementById("blankCanvas").toDataURL())
//display and animate plane
$("#canvas3d").css('display','block')
animePlane()
//moving plane at the end
setInterval(function(){
$("#canvas3d").css('transition','transform 2500ms ease-in-out')
$("#canvas3d").css('transform','translate(50vw, -150vh)')
},4400);
//reloading page (to change)
setInterval(function(){
window.location.href = "http://127.0.0.1:8887/?plane=1FTMbm9V9eindbwoJ9uW";
},5500);
})
});
})