sdrweb: improve adsb.hq.c3d2.de

This commit is contained in:
Astro 2021-10-13 23:44:24 +02:00
parent d3155ea261
commit 227a8827ee
3 changed files with 180 additions and 178 deletions

View File

@ -1,199 +1,199 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
<head> <head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" /> <title>ADS-B planespotting at C3D2.de</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css" integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==" crossorigin=""/> <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<style type="text/css"> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css" integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==" crossorigin=""/>
html { height: 100% } <style type="text/css">
body { height: 100%; margin: 0; padding: 0 } html { height: 100% }
.plane-icon { body { height: 100%; margin: 0; padding: 0 }
padding:0px; .plane-icon {
margin:0px; padding:0px;
} margin:0px;
#map_canvas { height: 100% } }
#info { #map_canvas { height: 100% }
position: absolute; #info {
width:20%; position: absolute;
height:100%; width:20%;
bottom:0px; height:100%;
right:0px; bottom:0px;
top:0px; right:0px;
background-color: white; top:0px;
border-left:1px #666 solid; background-color: white;
font-family:Helvetica; border-left:1px #666 solid;
} font-family:Helvetica;
#info div { }
padding:0px; #info div {
padding-left:10px; padding:0px;
margin:0px; padding-left:10px;
} margin:0px;
#info div h1 { }
margin-top:10px; #info div h1 {
font-size:16px; margin-top:10px;
} font-size:16px;
#info div p { }
font-size:14px; #info div p {
color:#333; font-size:14px;
} color:#333;
</style> }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script> </style>
<script src="https://unpkg.com/leaflet@1.6.0/dist/leaflet.js" integrity="sha512-gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQtXPSiUTtC0TjtGOmxa1AJPuV0CPthew==" crossorigin=""></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script type="text/javascript"> <script src="https://unpkg.com/leaflet@1.6.0/dist/leaflet.js" integrity="sha512-gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQtXPSiUTtC0TjtGOmxa1AJPuV0CPthew==" crossorigin=""></script>
Map=null; <script type="text/javascript">
CenterLat=45.0; Map=null;
CenterLon=9.0; Planes={};
Planes={}; NumPlanes = 0;
NumPlanes = 0; Selected=null
Selected=null
function getIconForPlane(plane) { function getIconForPlane(plane) {
var r = 255, g = 255, b = 0; var r = 255, g = 255, b = 0;
var maxalt = 40000; /* Max altitude in the average case */ var maxalt = 40000; /* Max altitude in the average case */
var invalt = maxalt-plane.altitude; var selected = (Selected == plane.hex);
var selected = (Selected == plane.hex);
if (invalt < 0) invalt = 0; b = 0.25 + (0.75/maxalt*plane.altitude);
b = parseInt(255/maxalt*invalt);
/* As Icon we use the plane emoji, this is a simple solution but /* As Icon we use the plane emoji, this is a simple solution but
is definitely a compromise: we expect the icon to be rotated is definitely a compromise: we expect the icon to be rotated
45 degrees facing north-east by default, this is true in most 45 degrees facing north-east by default, this is true in most
systems but not all. */ systems but not all. */
var he = document.createElement("P"); var he = document.createElement("P");
he.innerHTML = '>'; he.innerHTML = '>';
var rotation = 45+360-plane.track; var rotation = 45+360-plane.track;
var selhtml = ''; var selhtml = '';
/* Give a border to the selected plane. */ /* Give a border to the selected plane. */
if (Selected == plane.hex) { if (Selected == plane.hex) {
selhtml = 'border:1px dotted #0000aa; border-radius:10px;'; selhtml = 'border: 5px solid #FFFF00; border-radius: 100%';
} else { } else {
selhtml = ''; selhtml = '';
} }
he = '<div style="transform: rotate(-'+rotation+'deg); '+selhtml+'">✈️</div>'; he = '<div style="transform: scale('+b+') rotate(-'+rotation+'deg)"><img src="airplane.svg" width="64" height="64" style="'+selhtml+'"></div>';
var icon = L.divIcon({html: he, className: 'plane-icon'}); var icon = L.divIcon({html: he, className: 'plane-icon'});
return icon; return icon;
} }
function selectPlane(planehex) { function selectPlane(planehex) {
if (!Planes[planehex]) return; if (!Planes[planehex]) return;
var old = Selected; var old = Selected;
Selected = planehex; Selected = planehex;
if (Planes[old]) { if (Planes[old]) {
/* Remove the highlight in the previously selected plane. */ /* Remove the highlight in the previously selected plane. */
Planes[old].marker.setIcon(getIconForPlane(Planes[old])); Planes[old].marker.setIcon(getIconForPlane(Planes[old]));
} }
Planes[Selected].marker.setIcon(getIconForPlane(Planes[Selected])); Planes[Selected].marker.setIcon(getIconForPlane(Planes[Selected]));
refreshSelectedInfo(); refreshSelectedInfo();
} }
/* Return a closure to caputure the 'hex' argument. This way we don't /* Return a closure to caputure the 'hex' argument. This way we don't
have to care about how Leaflet passes the object to the callback. */ have to care about how Leaflet passes the object to the callback. */
function selectPlaneCallback(hex) { function selectPlaneCallback(hex) {
return function() { return function() {
return selectPlane(hex); return selectPlane(hex);
} }
} }
function refreshGeneralInfo() {
var i = document.getElementById('geninfo');
i.innerHTML = NumPlanes+' planes on screen.'; function refreshGeneralInfo() {
} var i = document.getElementById('geninfo');
function refreshSelectedInfo() { i.innerHTML = NumPlanes+' planes on screen.';
var i = document.getElementById('selinfo'); }
var p = Planes[Selected];
if (!p) return; function refreshSelectedInfo() {
var html = 'ICAO: '+p.hex+'<br>'; var i = document.getElementById('selinfo');
if (p.flight.length) { var p = Planes[Selected];
html += '<b>'+p.flight+'</b><br>';
}
html += 'Altitude: '+p.altitude+' feet<br>';
html += 'Speed: '+p.speed+' knots<br>';
html += 'Coordinates: '+p.lat+', '+p.lon+'<br>';
i.innerHTML = html;
}
function fetchData() { if (!p) return;
$.getJSON('/data.json', function(data) { var html = 'ICAO: '+p.hex+'<br>';
var stillhere = {} if (p.flight.length) {
for (var j=0; j < data.length; j++) { html += '<b>'+p.flight+'</b><br>';
var plane = data[j]; }
var marker = null; html += 'Altitude: '+p.altitude+'ft, ' + Math.round(p.altitude * 0.3048) + 'm<br>';
stillhere[plane.hex] = true; html += 'Speed: '+p.speed+'kts, ' + Math.round(p.speed * 1.852) + 'km/h<br>';
plane.flight = $.trim(plane.flight); html += 'Coordinates: '+p.lat+', '+p.lon+'<br>';
i.innerHTML = html;
}
if (Planes[plane.hex]) { function fetchData() {
var myplane = Planes[plane.hex]; $.getJSON('/data.json', function(data) {
marker = myplane.marker; var stillhere = {}
marker.setLatLng([plane.lat,plane.lon]); for (var j=0; j < data.length; j++) {
marker.setIcon(getIconForPlane(plane)); var plane = data[j];
myplane.altitude = plane.altitude; var marker = null;
myplane.speed = plane.speed; stillhere[plane.hex] = true;
myplane.lat = plane.lat; plane.flight = $.trim(plane.flight);
myplane.lon = plane.lon;
myplane.track = plane.track;
myplane.flight = plane.flight;
if (myplane.hex == Selected)
refreshSelectedInfo();
} else {
var icon = getIconForPlane(plane);
var marker = L.marker([plane.lat, plane.lon], {icon: icon}).addTo(Map);
var hex = plane.hex;
marker.on('click',selectPlaneCallback(plane.hex));
plane.marker = marker;
marker.planehex = plane.hex;
Planes[plane.hex] = plane;
}
// FIXME: Set the title if (Planes[plane.hex]) {
// if (plane.flight.length == 0) var myplane = Planes[plane.hex];
// marker.setTitle(plane.hex) marker = myplane.marker;
// else marker.setLatLng([plane.lat,plane.lon]);
// marker.setTitle(plane.flight+' ('+plane.hex+')') marker.setIcon(getIconForPlane(plane));
} myplane.altitude = plane.altitude;
NumPlanes = data.length; myplane.speed = plane.speed;
myplane.lat = plane.lat;
myplane.lon = plane.lon;
myplane.track = plane.track;
myplane.flight = plane.flight;
if (myplane.hex == Selected)
refreshSelectedInfo();
} else {
var icon = getIconForPlane(plane);
var marker = L.marker([plane.lat, plane.lon], {icon: icon}).addTo(Map);
var hex = plane.hex;
marker.on('click',selectPlaneCallback(plane.hex));
plane.marker = marker;
marker.planehex = plane.hex;
Planes[plane.hex] = plane;
}
/* Remove idle planes. */ // FIXME: Set the title
for (var p in Planes) { // if (plane.flight.length == 0)
if (!stillhere[p]) { // marker.setTitle(plane.hex)
Map.removeLayer(Planes[p].marker); // else
delete Planes[p]; // marker.setTitle(plane.flight+' ('+plane.hex+')')
} }
} NumPlanes = data.length;
});
}
function initialize() { /* Remove idle planes. */
Map = L.map('map_canvas').setView([51.08102, 13.72806], 8); for (var p in Planes) {
if (!stillhere[p]) {
Map.removeLayer(Planes[p].marker);
delete Planes[p];
}
}
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { refreshGeneralInfo();
attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
maxZoom: 18,
id: 'mapbox/streets-v11',
accessToken: 'your.mapbox.access.token'
}).addTo(Map);
/* Setup our timer to poll from the server. */ window.setTimeout(fetchData, 500);
window.setInterval(function() { });
fetchData(); }
refreshGeneralInfo();
}, 100);
}
</script> function initialize() {
</head> Map = L.map('map_canvas').setView([51.08102, 13.72806], 8);
<body onload="initialize()">
<div id="map_canvas" style="width:80%; height:100%"></div> L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
<div id="info"> attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
<div> maxZoom: 18,
<h1>Dump1090</h1> id: 'mapbox/streets-v11',
<p id="geninfo"></p> accessToken: 'your.mapbox.access.token'
<p id="selinfo">Click on a plane for info.</p> }).addTo(Map);
</div>
</div> /* Setup our timer to poll from the server. */
</body> window.setInterval(function() {
fetchData();
}, 500);
}
</script>
</head>
<body onload="initialize()">
<div id="map_canvas" style="width:80%; height:100%"></div>
<div id="info">
<div>
<h1>Dump1090</h1>
<p id="geninfo"></p>
<p id="selinfo">Click on a plane for info.</p>
</div>
</div>
</body>
</html> </html>

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 300 375" style="enable-background:new 0 0 300 300;" xml:space="preserve"><g><g><path d="M149.997,0C67.158,0,0.003,67.161,0.003,149.997S67.158,300,149.997,300s150-67.163,150-150.003S232.837,0,149.997,0z M222.978,99.461l-32.435,30.711l20.562,82.844l-12.27,12.27l-39.262-64.028l-0.905-0.905l-40.385,38.24 c-0.228,0.231-0.485,0.405-0.718,0.622l-1.297,29.481l-44.965-44.962l29.471-1.294c0.218-0.239,0.394-0.493,0.625-0.724 l38.24-40.387L139.314,141l-64.601-39.832l12.27-12.27l82.471,20.946l31.079-32.827c6.201-6.201,16.251-6.199,22.447,0 C229.177,83.215,229.177,93.263,222.978,99.461z"/></g></g><text x="0" y="315" fill="#000000" font-size="5px" font-weight="bold" font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif">Created by egi enggryawan</text><text x="0" y="320" fill="#000000" font-size="5px" font-weight="bold" font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif">from the Noun Project</text></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -34,6 +34,7 @@
root = pkgs.runCommandNoCC "adsb-map" {} '' root = pkgs.runCommandNoCC "adsb-map" {} ''
mkdir $out mkdir $out
cp ${./adsb.html} $out/index.html cp ${./adsb.html} $out/index.html
cp ${./airplane.svg} $out/airplane.svg
''; '';
extraConfig = '' extraConfig = ''
index index.html; index index.html;