browser based map: add AI traffic layer
Add a layer showing AI traffic (AI aircraft and multiplayer)
This commit is contained in:
parent
b2b6750d37
commit
e72fa5a702
1 changed files with 201 additions and 35 deletions
|
@ -6,14 +6,16 @@
|
|||
<link rel="stylesheet"
|
||||
href="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.css" />
|
||||
<script src="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.js"></script>
|
||||
<script
|
||||
src="../3rdparty/flot/jquery.flot.js"></script>
|
||||
<script src="../3rdparty/flot/jquery.flot.js"></script>
|
||||
<script src="../lib/props.js" type="text/javascript"></script>
|
||||
<script src="../lib/jquery.flot.prop.js"></script>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="description" content="FlightGear - Map" />
|
||||
<meta name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
|
||||
<title>FlightGear - Map</title>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -60,39 +62,38 @@ html,body,#map {
|
|||
height: 200px;
|
||||
}
|
||||
|
||||
|
||||
.axisLabel {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.yaxisLabel {
|
||||
top: 50%;
|
||||
left: 2px;
|
||||
transform: rotate(-90deg);
|
||||
-o-transform: rotate(-90deg);
|
||||
-ms-transform: rotate(-90deg);
|
||||
-moz-transform: rotate(-90deg);
|
||||
-webkit-transform: rotate(-90deg);
|
||||
transform-origin: 0 0;
|
||||
-o-transform-origin: 0 0;
|
||||
-ms-transform-origin: 0 0;
|
||||
-moz-transform-origin: 0 0;
|
||||
-webkit-transform-origin: 0 0;
|
||||
top: 50%;
|
||||
left: 2px;
|
||||
transform: rotate(-90deg);
|
||||
-o-transform: rotate(-90deg);
|
||||
-ms-transform: rotate(-90deg);
|
||||
-moz-transform: rotate(-90deg);
|
||||
-webkit-transform: rotate(-90deg);
|
||||
transform-origin: 0 0;
|
||||
-o-transform-origin: 0 0;
|
||||
-ms-transform-origin: 0 0;
|
||||
-moz-transform-origin: 0 0;
|
||||
-webkit-transform-origin: 0 0;
|
||||
}
|
||||
|
||||
.xaxisLabel {
|
||||
top: 90%;
|
||||
left: 45%;
|
||||
top: 90%;
|
||||
left: 45%;
|
||||
}
|
||||
|
||||
.ie7 .yaxisLabel, .ie8 .yaxisLabel {
|
||||
top: 40%;
|
||||
font-size: 36px;
|
||||
filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.0, M12=0.33, M21=-0.33, M22=0.0,sizingMethod='auto expand');
|
||||
.ie7 .yaxisLabel,.ie8 .yaxisLabel {
|
||||
top: 40%;
|
||||
font-size: 36px;
|
||||
filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.0, M12=0.33, M21=-0.33,
|
||||
M22=0.0, sizingMethod='auto expand');
|
||||
}
|
||||
|
||||
</style>
|
||||
<div id='map'></div>
|
||||
<script type="text/javascript">
|
||||
|
@ -193,6 +194,13 @@ html,body,#map {
|
|||
popupAncor : [ 0, -17 ],
|
||||
iconUrl : "images/arp.svg",
|
||||
});
|
||||
MAP_ICON["aircraft"] = L.icon({
|
||||
iconSize : [ 20, 20 ],
|
||||
iconAnchor : [ 10, 10 ],
|
||||
popupAncor : [ 0, -12 ],
|
||||
iconUrl : "images/aircraft.svg",
|
||||
});
|
||||
|
||||
L.RotatedMarker = L.Marker.extend({
|
||||
options : {
|
||||
angle : 0
|
||||
|
@ -336,6 +344,167 @@ html,body,#map {
|
|||
|
||||
map.addLayer(navdbLayer);
|
||||
|
||||
var aiLayer = new L.geoJson(null, {
|
||||
pointToLayer : function(feature, latlng) {
|
||||
var options = {
|
||||
title : feature.id + ' (' + feature.properties.name + ')',
|
||||
alt : feature.id,
|
||||
riseOnHover : true,
|
||||
};
|
||||
|
||||
if (feature.properties.type == "aircraft" || feature.properties.type == "multiplayer") {
|
||||
options.angle = feature.properties.heading;
|
||||
options.icon = MAP_ICON["aircraft"];
|
||||
}
|
||||
|
||||
return new L.RotatedMarker(latlng, options);
|
||||
},
|
||||
onEachFeature : function(feature, layer) {
|
||||
if (feature.properties) {
|
||||
var popupString = '<div class="popup">';
|
||||
for ( var k in feature.properties) {
|
||||
var v = feature.properties[k];
|
||||
popupString += k + ': ' + v + '<br />';
|
||||
}
|
||||
popupString += '</div>';
|
||||
layer.bindPopup(popupString, {
|
||||
maxHeight : 200
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
filter : function(feature, layer) {
|
||||
return true;
|
||||
},
|
||||
|
||||
style : function(feature) {
|
||||
if (feature.properties.type == "airport") {
|
||||
return {
|
||||
color : 'black',
|
||||
weight : 3,
|
||||
fill : 'true',
|
||||
fillColor : '#606060',
|
||||
fillOpacity : 1.0,
|
||||
lineJoin : 'bevel',
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
aiLayer.aiPropsToGeoJson = function(props, types) {
|
||||
var geoJSON = {
|
||||
type : "FeatureCollection",
|
||||
features : [],
|
||||
};
|
||||
|
||||
var getChild = function(p, n, idx) {
|
||||
if (p && p.children) {
|
||||
if (idx == null)
|
||||
idx = 0;
|
||||
for (var i = 0; i < p.children.length; i++) {
|
||||
var child = p.children[i];
|
||||
if (child.name == n && child.index == idx) {
|
||||
return child;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
var getChildren = function(p, n) {
|
||||
var reply = [];
|
||||
if (p && p.children) {
|
||||
p.children.forEach(function(child) {
|
||||
if (child.name == n)
|
||||
reply.push(child);
|
||||
});
|
||||
}
|
||||
return reply;
|
||||
};
|
||||
|
||||
if (props) {
|
||||
types.forEach(function(type) {
|
||||
var aiModels = getChildren(props, type);
|
||||
aiModels.forEach(function(aiModel) {
|
||||
var lat = -999;
|
||||
var lon = -999;
|
||||
var heading = 0;
|
||||
var tas = 0;
|
||||
var vspeed = 0;
|
||||
var callsign = "";
|
||||
|
||||
var n = getChild(aiModel, "callsign");
|
||||
if (n)
|
||||
callsign = n.value;
|
||||
|
||||
var positionNode = getChild(aiModel, "position");
|
||||
if (positionNode) {
|
||||
var n = getChild(positionNode, "latitude-deg");
|
||||
if (n)
|
||||
lat = n.value;
|
||||
var n = getChild(positionNode, "longitude-deg");
|
||||
if (n)
|
||||
lon = n.value;
|
||||
}
|
||||
|
||||
var orientationNode = getChild(aiModel, "orientation");
|
||||
if (orientationNode) {
|
||||
var n = getChild(orientationNode, "true-heading-deg");
|
||||
heading = n.value;
|
||||
}
|
||||
|
||||
var velocitiesNode = getChild(aiModel, "velocities");
|
||||
if (velocitiesNode) {
|
||||
var n = getChild(orientationNode, "true-airspeed-kt");
|
||||
if (n)
|
||||
tas = n.value;
|
||||
var n = getChild(orientationNode, "vertical-speed-fps");
|
||||
if (n)
|
||||
vspeed = n.value;
|
||||
}
|
||||
|
||||
geoJSON.features.push({
|
||||
"type" : "Feature",
|
||||
"geometry" : {
|
||||
"type" : "Point",
|
||||
"coordinates" : [ lon, lat ],
|
||||
},
|
||||
"properties" : {
|
||||
"type" : aiModel.name,
|
||||
"heading" : heading,
|
||||
"tas" : tas,
|
||||
"vspeed" : vspeed,
|
||||
"id" : callsign,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return geoJSON;
|
||||
};
|
||||
|
||||
aiLayer.update = function() {
|
||||
// fetch the entire property subtree under /ai/models
|
||||
var url = "/json/ai/models?d=99";
|
||||
|
||||
var jqxhr = $.get(url).done(function(data) {
|
||||
var types = [ "aircraft", "multiplayer", "carrier" ];
|
||||
aiLayer.clearLayers();
|
||||
aiLayer.addData(aiLayer.aiPropsToGeoJson(data, types));
|
||||
}).fail(function() {
|
||||
alert('failed to load multiplayer data');
|
||||
}).always(function() {
|
||||
});
|
||||
|
||||
setTimeout(function() {
|
||||
aiLayer.update()
|
||||
}, 5000);
|
||||
};
|
||||
|
||||
map.addLayer(aiLayer);
|
||||
|
||||
var baseLayers = {
|
||||
"OpenStreetMaps" : osm,
|
||||
"MapQuest Satelite" : mqsat,
|
||||
|
@ -344,6 +513,7 @@ html,body,#map {
|
|||
|
||||
var overlays = {
|
||||
"NAVDB" : navdbLayer,
|
||||
"AI" : aiLayer,
|
||||
//"ICAO VFR (Germany)" : icaoGermany,
|
||||
//"Lower Airspace (Germany)" : lowerGermany,
|
||||
"Precipitation" : owmPrecipitation,
|
||||
|
@ -394,7 +564,6 @@ html,body,#map {
|
|||
position : 'bottomright'
|
||||
});
|
||||
|
||||
|
||||
altitudePlotter.onAdd = function(map) {
|
||||
this._div = L.DomUtil.create('div', 'altitudePlotter');
|
||||
this._div.innerHTML = '<h4>Altitude</h4>';
|
||||
|
@ -404,20 +573,20 @@ html,body,#map {
|
|||
altitudePlotter.initPlotter = function(map) {
|
||||
var container = $(".altitudePlotter");
|
||||
var w = $("#map").innerWidth() * .9;
|
||||
container.css('width', w );
|
||||
container.css('width', w);
|
||||
this.maxData = container.outerWidth() / 2 || 300;
|
||||
|
||||
var series = [ {
|
||||
propertyPath : "/position/altitude-ft",
|
||||
data : [],
|
||||
color: 'blue',
|
||||
color : 'blue',
|
||||
lines : {
|
||||
fill : true
|
||||
}
|
||||
} ];
|
||||
|
||||
this.plot = $.plot(container, series, {
|
||||
historyLength: 600,
|
||||
historyLength : 600,
|
||||
grid : {
|
||||
borderWidth : 1,
|
||||
minBorderMargin : 20,
|
||||
|
@ -444,13 +613,9 @@ html,body,#map {
|
|||
}
|
||||
});
|
||||
|
||||
this.yaxisLabel = $("<div class='axisLabel yaxisLabel'></div>")
|
||||
.text("Altitude (ft)")
|
||||
.appendTo(container);
|
||||
this.yaxisLabel = $("<div class='axisLabel yaxisLabel'></div>").text("Altitude (ft)").appendTo(container);
|
||||
|
||||
this.xaxisLabel = $("<div class='axisLabel xaxisLabel'></div>")
|
||||
.text("Simulation time (s)")
|
||||
.appendTo(container);
|
||||
this.xaxisLabel = $("<div class='axisLabel xaxisLabel'></div>").text("Simulation time (s)").appendTo(container);
|
||||
};
|
||||
|
||||
altitudePlotter.addTo(map);
|
||||
|
@ -504,6 +669,7 @@ html,body,#map {
|
|||
};
|
||||
|
||||
navdbLayer.update();
|
||||
aiLayer.update();
|
||||
|
||||
var latlng;
|
||||
var i = 0;
|
||||
|
|
Loading…
Reference in a new issue