This commit is contained in:
parent
05e9908b89
commit
372f7b4f51
349
gmap.html
349
gmap.html
@ -1,199 +1,194 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css" integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ==" crossorigin=""/>
|
||||
<style type="text/css">
|
||||
html { height: 100% }
|
||||
body { height: 100%; margin: 0; padding: 0 }
|
||||
.plane-icon {
|
||||
padding:0px;
|
||||
margin:0px;
|
||||
}
|
||||
#map_canvas { height: 100% }
|
||||
#info {
|
||||
position: absolute;
|
||||
width:20%;
|
||||
height:100%;
|
||||
bottom:0px;
|
||||
right:0px;
|
||||
top:0px;
|
||||
background-color: white;
|
||||
border-left:1px #666 solid;
|
||||
font-family:Helvetica;
|
||||
}
|
||||
#info div {
|
||||
padding:0px;
|
||||
padding-left:10px;
|
||||
margin:0px;
|
||||
}
|
||||
#info div h1 {
|
||||
margin-top:10px;
|
||||
font-size:16px;
|
||||
}
|
||||
#info div p {
|
||||
font-size:14px;
|
||||
color:#333;
|
||||
}
|
||||
</style>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
|
||||
<script src="https://unpkg.com/leaflet@1.6.0/dist/leaflet.js" integrity="sha512-gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQtXPSiUTtC0TjtGOmxa1AJPuV0CPthew==" crossorigin=""></script>
|
||||
|
||||
<head>
|
||||
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css"
|
||||
integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ=="
|
||||
crossorigin="" />
|
||||
<style type="text/css">
|
||||
html {
|
||||
height: 100%
|
||||
}
|
||||
|
||||
body {
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0
|
||||
}
|
||||
|
||||
#map_canvas {
|
||||
height: 100%
|
||||
}
|
||||
|
||||
</style>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
|
||||
<script src="https://unpkg.com/leaflet@1.6.0/dist/leaflet.js"
|
||||
integrity="sha512-gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQtXPSiUTtC0TjtGOmxa1AJPuV0CPthew=="
|
||||
crossorigin=""></script>
|
||||
<script type="text/javascript">
|
||||
Map=null;
|
||||
CenterLat=45.0;
|
||||
CenterLon=9.0;
|
||||
Planes={};
|
||||
NumPlanes = 0;
|
||||
Selected=null
|
||||
//Leaflet.RotatedMarker 库
|
||||
(function() {
|
||||
// save these original methods before they are overwritten
|
||||
var proto_initIcon = L.Marker.prototype._initIcon;
|
||||
var proto_setPos = L.Marker.prototype._setPos;
|
||||
|
||||
function getIconForPlane(plane) {
|
||||
var r = 255, g = 255, b = 0;
|
||||
var maxalt = 40000; /* Max altitude in the average case */
|
||||
var invalt = maxalt-plane.altitude;
|
||||
var selected = (Selected == plane.hex);
|
||||
var oldIE = (L.DomUtil.TRANSFORM === 'msTransform');
|
||||
|
||||
if (invalt < 0) invalt = 0;
|
||||
b = parseInt(255/maxalt*invalt);
|
||||
|
||||
/* As Icon we use the plane emoji, this is a simple solution but
|
||||
is definitely a compromise: we expect the icon to be rotated
|
||||
45 degrees facing north-east by default, this is true in most
|
||||
systems but not all. */
|
||||
var he = document.createElement("P");
|
||||
he.innerHTML = '>';
|
||||
var rotation = 45+360-plane.track;
|
||||
var selhtml = '';
|
||||
|
||||
/* Give a border to the selected plane. */
|
||||
if (Selected == plane.hex) {
|
||||
selhtml = 'border:1px dotted #0000aa; border-radius:10px;';
|
||||
} else {
|
||||
selhtml = '';
|
||||
L.Marker.addInitHook(function () {
|
||||
var iconOptions = this.options.icon && this.options.icon.options;
|
||||
var iconAnchor = iconOptions && this.options.icon.options.iconAnchor;
|
||||
if (iconAnchor) {
|
||||
iconAnchor = (iconAnchor[0] + 'px ' + iconAnchor[1] + 'px');
|
||||
}
|
||||
he = '<div style="transform: rotate(-'+rotation+'deg); '+selhtml+'">✈️</div>';
|
||||
var icon = L.divIcon({html: he, className: 'plane-icon'});
|
||||
return icon;
|
||||
}
|
||||
this.options.rotationOrigin = this.options.rotationOrigin || iconAnchor || 'center bottom' ;
|
||||
this.options.rotationAngle = this.options.rotationAngle || 0;
|
||||
|
||||
function selectPlane(planehex) {
|
||||
if (!Planes[planehex]) return;
|
||||
var old = Selected;
|
||||
Selected = planehex;
|
||||
if (Planes[old]) {
|
||||
/* Remove the highlight in the previously selected plane. */
|
||||
Planes[old].marker.setIcon(getIconForPlane(Planes[old]));
|
||||
}
|
||||
Planes[Selected].marker.setIcon(getIconForPlane(Planes[Selected]));
|
||||
refreshSelectedInfo();
|
||||
}
|
||||
// Ensure marker keeps rotated during dragging
|
||||
this.on('drag', function(e) { e.target._applyRotation(); });
|
||||
});
|
||||
|
||||
/* 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. */
|
||||
function selectPlaneCallback(hex) {
|
||||
return function() {
|
||||
return selectPlane(hex);
|
||||
}
|
||||
}
|
||||
|
||||
function refreshGeneralInfo() {
|
||||
var i = document.getElementById('geninfo');
|
||||
L.Marker.include({
|
||||
_initIcon: function() {
|
||||
proto_initIcon.call(this);
|
||||
},
|
||||
|
||||
i.innerHTML = NumPlanes+' planes on screen.';
|
||||
}
|
||||
_setPos: function (pos) {
|
||||
proto_setPos.call(this, pos);
|
||||
this._applyRotation();
|
||||
},
|
||||
|
||||
function refreshSelectedInfo() {
|
||||
var i = document.getElementById('selinfo');
|
||||
var p = Planes[Selected];
|
||||
_applyRotation: function () {
|
||||
if(this.options.rotationAngle) {
|
||||
this._icon.style[L.DomUtil.TRANSFORM+'Origin'] = this.options.rotationOrigin;
|
||||
|
||||
if (!p) return;
|
||||
var html = 'ICAO: '+p.hex+'<br>';
|
||||
if (p.flight.length) {
|
||||
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() {
|
||||
$.getJSON('/data.json', function(data) {
|
||||
var stillhere = {}
|
||||
for (var j=0; j < data.length; j++) {
|
||||
var plane = data[j];
|
||||
var marker = null;
|
||||
stillhere[plane.hex] = true;
|
||||
plane.flight = $.trim(plane.flight);
|
||||
|
||||
if (Planes[plane.hex]) {
|
||||
var myplane = Planes[plane.hex];
|
||||
marker = myplane.marker;
|
||||
marker.setLatLng([plane.lat,plane.lon]);
|
||||
marker.setIcon(getIconForPlane(plane));
|
||||
myplane.altitude = plane.altitude;
|
||||
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();
|
||||
if(oldIE) {
|
||||
// for IE 9, use the 2D rotation
|
||||
this._icon.style[L.DomUtil.TRANSFORM] = 'rotate(' + this.options.rotationAngle + 'deg)';
|
||||
} 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 (plane.flight.length == 0)
|
||||
// marker.setTitle(plane.hex)
|
||||
// else
|
||||
// marker.setTitle(plane.flight+' ('+plane.hex+')')
|
||||
}
|
||||
NumPlanes = data.length;
|
||||
|
||||
/* Remove idle planes. */
|
||||
for (var p in Planes) {
|
||||
if (!stillhere[p]) {
|
||||
Map.removeLayer(Planes[p].marker);
|
||||
delete Planes[p];
|
||||
// for modern browsers, prefer the 3D accelerated version
|
||||
this._icon.style[L.DomUtil.TRANSFORM] += ' rotateZ(' + this.options.rotationAngle + 'deg)';
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
setRotationAngle: function(angle) {
|
||||
this.options.rotationAngle = angle;
|
||||
this.update();
|
||||
return this;
|
||||
},
|
||||
|
||||
setRotationOrigin: function(origin) {
|
||||
this.options.rotationOrigin = origin;
|
||||
this.update();
|
||||
return this;
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
Map = null;
|
||||
CenterLat = 45.0;
|
||||
CenterLon = 9.0;
|
||||
Planes = {};
|
||||
NumPlanes = 0;
|
||||
let planeSVG='data:image/svg+xml;charset=utf-8,%3Csvg%20alt%3D%22Airliner%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2025%2026%22%20width%3D%2225px%22%20height%3D%2226px%22%20class%3D%22flightPageAircraftIcon%20Enroute%20progressBarAircraftIcon%22%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill%3A%23FFFFFF%3B%7D.cls-2%7Bfill%3A%235A5A5A%3B%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Ctitle%3Eairliner_live%3C%2Ftitle%3E%3Cg%20id%3D%22Layer_2%22%20data-name%3D%22Layer%202%22%3E%3Cg%20id%3D%22Airliner%22%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M12.51%2C25.75c-.26%2C0-.74-.71-.86-1.41l-3.33.86L8%2C25.29l.08-1.41.11-.07c1.13-.68%2C2.68-1.64%2C3.2-2-.37-1.06-.51-3.92-.43-8.52v0L8%2C13.31C5.37%2C14.12%2C1.2%2C15.39%2C1%2C15.5a.5.5%2C0%2C0%2C1-.21%2C0%2C.52.52%2C0%2C0%2C1-.49-.45%2C1%2C1%2C0%2C0%2C1%2C.52-1l1.74-.91c1.36-.71%2C3.22-1.69%2C4.66-2.43a4%2C4%2C0%2C0%2C1%2C0-.52c0-.69%2C0-1%2C0-1.14l.25-.13H7.16A1.07%2C1.07%2C0%2C0%2C1%2C8.24%2C7.73%2C1.12%2C1.12%2C0%2C0%2C1%2C9.06%2C8a1.46%2C1.46%2C0%2C0%2C1%2C.26.87L9.08%2C9h.25c0%2C.14%2C0%2C.31%2C0%2C.58l1.52-.84c0-1.48%2C0-7.06%2C1.1-8.25a.74.74%2C0%2C0%2C1%2C1.13%2C0c1.15%2C1.19%2C1.13%2C6.78%2C1.1%2C8.25l1.52.84c0-.32%2C0-.48%2C0-.58l.25-.13H15.7A1.46%2C1.46%2C0%2C0%2C1%2C16%2C8a1.11%2C1.11%2C0%2C0%2C1%2C.82-.28%2C1.06%2C1.06%2C0%2C0%2C1%2C1.08%2C1.16V9c0%2C.19%2C0%2C.48%2C0%2C1.17a4%2C4%2C0%2C0%2C1%2C0%2C.52c1.75.9%2C4.4%2C2.29%2C5.67%2C3l.73.38a.9.9%2C0%2C0%2C1%2C.5%2C1%2C.55.55%2C0%2C0%2C1-.5.47h0l-.11%2C0c-.28-.11-4.81-1.49-7.16-2.2H14.06v0c.09%2C4.6-.06%2C7.46-.43%2C8.52.52.33%2C2.07%2C1.29%2C3.2%2C2l.11.07L17%2C25.29l-.33-.09-3.33-.86c-.12.7-.6%2C1.41-.86%2C1.41h0Z%22%2F%3E%3Cpath%20class%3D%22cls-2%22%20d%3D%22M12.51.5C13.93.5%2C14%2C7%2C13.93%2C8.91c.3.16%2C1.64.91%2C2%2C1.1%2C0-.6%2C0-.85%2C0-1s0-.09%2C0-.13a1.18%2C1.18%2C0%2C0%2C1%2C.19-.7A.88.88%2C0%2C0%2C1%2C16.78%2C8h0a.82.82%2C0%2C0%2C1%2C.83.91s0%2C.07%2C0%2C.13%2C0%2C.44%2C0%2C1.17a3.21%2C3.21%2C0%2C0%2C1-.06.66c2.33%2C1.19%2C6.51%2C3.39%2C6.56%2C3.42.59.3.4%2C1%2C.11%2C1h-.07c-.37-.14-7.18-2.21-7.18-2.21l-3.18%2C0c0%2C.22.22%2C7.56-.48%2C8.91%2C0%2C0%2C2%2C1.26%2C3.39%2C2.08l.06.93L13.15%2C24a2.14%2C2.14%2C0%2C0%2C1-.64%2C1.47A2.14%2C2.14%2C0%2C0%2C1%2C11.87%2C24L8.26%2C25%2C8.31%2C24c1.38-.82%2C3.39-2.08%2C3.39-2.08-.7-1.35-.48-8.69-.48-8.91L8%2C13.06S1.17%2C15.13.86%2C15.27l-.11%2C0c-.32%2C0-.43-.73.14-1S5.13%2C12%2C7.46%2C10.85a3.21%2C3.21%2C0%2C0%2C1-.06-.66c0-.73%2C0-1%2C0-1.17s0-.09%2C0-.13A.82.82%2C0%2C0%2C1%2C8.24%2C8h0a.88.88%2C0%2C0%2C1%2C.65.21%2C1.18%2C1.18%2C0%2C0%2C1%2C.19.7s0%2C.07%2C0%2C.13%2C0%2C.39%2C0%2C1c.36-.19%2C1.71-.94%2C2-1.1C11.05%2C7%2C11.09.5%2C12.51.5m0-.5a1%2C1%2C0%2C0%2C0-.74.34c-1.16%2C1.2-1.2%2C6.3-1.18%2C8.28L10%2C8.93l-.46.25V8.91a1.68%2C1.68%2C0%2C0%2C0-.33-1.06%2C1.34%2C1.34%2C0%2C0%2C0-1-.36%2C1.31%2C1.31%2C0%2C0%2C0-1.33%2C1.4V9h0v0c0%2C.16%2C0%2C.46%2C0%2C1.14%2C0%2C.13%2C0%2C.26%2C0%2C.38l-4.5%2C2.35-1.74.91A1.2%2C1.2%2C0%2C0%2C0%2C0%2C15.15a.77.77%2C0%2C0%2C0%2C.73.64.74.74%2C0%2C0%2C0%2C.31-.07c.29-.12%2C4.35-1.35%2C7-2.17l2.6%2C0c-.1%2C5.54.17%2C7.46.38%2C8.2-.64.4-2%2C1.25-3%2C1.86l-.22.13%2C0%2C.26-.06.93%2C0%2C.81.7-.31%2C3.06-.79c.19.67.63%2C1.35%2C1%2C1.35s.86-.68%2C1-1.35l3.06.79.7.31%2C0-.81L17.2%2C24l0-.26L17%2C23.6c-1-.61-2.4-1.47-3-1.86.21-.74.48-2.66.38-8.2l2.6%2C0c2.72.83%2C6.81%2C2.07%2C7.07%2C2.18a.68.68%2C0%2C0%2C0%2C.25%2C0%2C.79.79%2C0%2C0%2C0%2C.74-.67%2C1.15%2C1.15%2C0%2C0%2C0-.63-1.29l-.71-.37c-1.23-.65-3.78-2-5.53-2.88%2C0-.12%2C0-.25%2C0-.38%2C0-.67%2C0-1%2C0-1.14h0V8.92a1.32%2C1.32%2C0%2C0%2C0-1.32-1.44%2C1.35%2C1.35%2C0%2C0%2C0-1%2C.36%2C1.67%2C1.67%2C0%2C0%2C0-.33%2C1V9h0v.22L15%2C8.93l-.57-.32c0-2%2C0-7.08-1.18-8.28A1%2C1%2C0%2C0%2C0%2C12.51%2C0Z%22%2F%3E%3C%2Fg%3E%3C%2Fg%3E%3C%2Fsvg%3E';
|
||||
function getIconForPlane(plane) {
|
||||
var r = 255, g = 255, b = 0;
|
||||
var maxalt = 40000; /* Max altitude in the average case */
|
||||
var invalt = maxalt - plane.altitude;
|
||||
|
||||
if (invalt < 0) invalt = 0;
|
||||
b = parseInt(255 / maxalt * invalt);
|
||||
var myIcon = L.icon({
|
||||
iconUrl: planeSVG,
|
||||
iconSize: [24, 24],
|
||||
className: 'plane-icon'
|
||||
});
|
||||
return myIcon;
|
||||
}
|
||||
function showPopup(plane){
|
||||
let machConst=1225.044; //1马赫常数
|
||||
let atmConst=1013.25;//标准大气压常数
|
||||
//设置回调
|
||||
return function(){
|
||||
let info=plane.flight+"<br/>"+
|
||||
"ICAO:"+plane.hex+"<br/>"+
|
||||
"高度:"+plane.altitude+" 米<br/>"+
|
||||
"速度:"+plane.speed+" km/h ("+(plane.speed/machConst).toFixed(2)+" 马赫)<br/>"+
|
||||
"气压: 约"+(atmConst-(plane.altitude*0.10936)).toFixed(2)+" hPa<br/>"+
|
||||
"航向:"+plane.track;
|
||||
L.popup()
|
||||
.setLatLng([plane.lat, plane.lon])
|
||||
.setContent(info)
|
||||
.openOn(Map);
|
||||
}
|
||||
}
|
||||
//更新数据
|
||||
function fetchData() {
|
||||
$.getJSON('/data.json', function (data) {
|
||||
var stillhere = {}
|
||||
for (var j = 0; j < data.length; j++) {
|
||||
var plane = data[j];
|
||||
var marker = null;
|
||||
stillhere[plane.hex] = true;
|
||||
plane.flight = $.trim(plane.flight);
|
||||
if (Planes[plane.hex]) {
|
||||
var myplane = Planes[plane.hex];
|
||||
marker = myplane.marker;
|
||||
marker.options.rotationAngle=plane.track; //添加航向
|
||||
marker.setLatLng([plane.lat, plane.lon]);
|
||||
marker.setIcon(getIconForPlane(plane));
|
||||
myplane.altitude = plane.altitude;
|
||||
myplane.speed = plane.speed;
|
||||
myplane.lat = plane.lat;
|
||||
myplane.lon = plane.lon;
|
||||
myplane.track = plane.track;
|
||||
myplane.flight = plane.flight;
|
||||
} else {
|
||||
var icon = getIconForPlane(plane);
|
||||
var marker = L.marker([plane.lat, plane.lon], { icon: icon ,title:plane.flight,alt:plane.flight,rotationAngle:plane.track}).addTo(Map);
|
||||
var hex = plane.hex;
|
||||
marker.on('click', showPopup(plane));//注册飞机点击事件
|
||||
plane.marker = marker;
|
||||
marker.planehex = plane.hex;
|
||||
Planes[plane.hex] = plane;
|
||||
}
|
||||
}
|
||||
NumPlanes = data.length;
|
||||
|
||||
/* 移除没用的飞机 */
|
||||
for (var p in Planes) {
|
||||
if (!stillhere[p]) {
|
||||
Map.removeLayer(Planes[p].marker);
|
||||
delete Planes[p];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initialize() {
|
||||
Map = L.map('map_canvas').setView([37.0, 13.0], 8);
|
||||
Map = L.map('map_canvas').setView([29.0, 121.0], 5);
|
||||
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: 'Map data © <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);
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
attribution: '使用 OpenStreetMap',
|
||||
maxZoom: 18,
|
||||
id: 'mapbox/streets-v11',
|
||||
accessToken: '0'
|
||||
}).addTo(Map);
|
||||
|
||||
/* Setup our timer to poll from the server. */
|
||||
window.setInterval(function() {
|
||||
fetchData();
|
||||
refreshGeneralInfo();
|
||||
}, 100);
|
||||
/* 定时刷新*/
|
||||
window.setInterval(function () {
|
||||
fetchData();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
</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>
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body onload="initialize()">
|
||||
<div id="map_canvas" style="width:100%; height:100%"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user