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"
|
<link rel="stylesheet"
|
||||||
href="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.css" />
|
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="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.js"></script>
|
||||||
<script
|
<script src="../3rdparty/flot/jquery.flot.js"></script>
|
||||||
src="../3rdparty/flot/jquery.flot.js"></script>
|
|
||||||
<script src="../lib/props.js" type="text/javascript"></script>
|
<script src="../lib/props.js" type="text/javascript"></script>
|
||||||
<script src="../lib/jquery.flot.prop.js"></script>
|
<script src="../lib/jquery.flot.prop.js"></script>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="description" content="FlightGear - Map" />
|
<meta name="description" content="FlightGear - Map" />
|
||||||
<meta name="viewport"
|
<meta name="viewport"
|
||||||
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
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>
|
<title>FlightGear - Map</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -60,39 +62,38 @@ html,body,#map {
|
||||||
height: 200px;
|
height: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.axisLabel {
|
.axisLabel {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.yaxisLabel {
|
.yaxisLabel {
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 2px;
|
left: 2px;
|
||||||
transform: rotate(-90deg);
|
transform: rotate(-90deg);
|
||||||
-o-transform: rotate(-90deg);
|
-o-transform: rotate(-90deg);
|
||||||
-ms-transform: rotate(-90deg);
|
-ms-transform: rotate(-90deg);
|
||||||
-moz-transform: rotate(-90deg);
|
-moz-transform: rotate(-90deg);
|
||||||
-webkit-transform: rotate(-90deg);
|
-webkit-transform: rotate(-90deg);
|
||||||
transform-origin: 0 0;
|
transform-origin: 0 0;
|
||||||
-o-transform-origin: 0 0;
|
-o-transform-origin: 0 0;
|
||||||
-ms-transform-origin: 0 0;
|
-ms-transform-origin: 0 0;
|
||||||
-moz-transform-origin: 0 0;
|
-moz-transform-origin: 0 0;
|
||||||
-webkit-transform-origin: 0 0;
|
-webkit-transform-origin: 0 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.xaxisLabel {
|
.xaxisLabel {
|
||||||
top: 90%;
|
top: 90%;
|
||||||
left: 45%;
|
left: 45%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ie7 .yaxisLabel, .ie8 .yaxisLabel {
|
.ie7 .yaxisLabel,.ie8 .yaxisLabel {
|
||||||
top: 40%;
|
top: 40%;
|
||||||
font-size: 36px;
|
font-size: 36px;
|
||||||
filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.0, M12=0.33, M21=-0.33, M22=0.0,sizingMethod='auto expand');
|
filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.0, M12=0.33, M21=-0.33,
|
||||||
|
M22=0.0, sizingMethod='auto expand');
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<div id='map'></div>
|
<div id='map'></div>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
@ -193,6 +194,13 @@ html,body,#map {
|
||||||
popupAncor : [ 0, -17 ],
|
popupAncor : [ 0, -17 ],
|
||||||
iconUrl : "images/arp.svg",
|
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({
|
L.RotatedMarker = L.Marker.extend({
|
||||||
options : {
|
options : {
|
||||||
angle : 0
|
angle : 0
|
||||||
|
@ -336,6 +344,167 @@ html,body,#map {
|
||||||
|
|
||||||
map.addLayer(navdbLayer);
|
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 = {
|
var baseLayers = {
|
||||||
"OpenStreetMaps" : osm,
|
"OpenStreetMaps" : osm,
|
||||||
"MapQuest Satelite" : mqsat,
|
"MapQuest Satelite" : mqsat,
|
||||||
|
@ -344,6 +513,7 @@ html,body,#map {
|
||||||
|
|
||||||
var overlays = {
|
var overlays = {
|
||||||
"NAVDB" : navdbLayer,
|
"NAVDB" : navdbLayer,
|
||||||
|
"AI" : aiLayer,
|
||||||
//"ICAO VFR (Germany)" : icaoGermany,
|
//"ICAO VFR (Germany)" : icaoGermany,
|
||||||
//"Lower Airspace (Germany)" : lowerGermany,
|
//"Lower Airspace (Germany)" : lowerGermany,
|
||||||
"Precipitation" : owmPrecipitation,
|
"Precipitation" : owmPrecipitation,
|
||||||
|
@ -394,7 +564,6 @@ html,body,#map {
|
||||||
position : 'bottomright'
|
position : 'bottomright'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
altitudePlotter.onAdd = function(map) {
|
altitudePlotter.onAdd = function(map) {
|
||||||
this._div = L.DomUtil.create('div', 'altitudePlotter');
|
this._div = L.DomUtil.create('div', 'altitudePlotter');
|
||||||
this._div.innerHTML = '<h4>Altitude</h4>';
|
this._div.innerHTML = '<h4>Altitude</h4>';
|
||||||
|
@ -404,20 +573,20 @@ html,body,#map {
|
||||||
altitudePlotter.initPlotter = function(map) {
|
altitudePlotter.initPlotter = function(map) {
|
||||||
var container = $(".altitudePlotter");
|
var container = $(".altitudePlotter");
|
||||||
var w = $("#map").innerWidth() * .9;
|
var w = $("#map").innerWidth() * .9;
|
||||||
container.css('width', w );
|
container.css('width', w);
|
||||||
this.maxData = container.outerWidth() / 2 || 300;
|
this.maxData = container.outerWidth() / 2 || 300;
|
||||||
|
|
||||||
var series = [ {
|
var series = [ {
|
||||||
propertyPath : "/position/altitude-ft",
|
propertyPath : "/position/altitude-ft",
|
||||||
data : [],
|
data : [],
|
||||||
color: 'blue',
|
color : 'blue',
|
||||||
lines : {
|
lines : {
|
||||||
fill : true
|
fill : true
|
||||||
}
|
}
|
||||||
} ];
|
} ];
|
||||||
|
|
||||||
this.plot = $.plot(container, series, {
|
this.plot = $.plot(container, series, {
|
||||||
historyLength: 600,
|
historyLength : 600,
|
||||||
grid : {
|
grid : {
|
||||||
borderWidth : 1,
|
borderWidth : 1,
|
||||||
minBorderMargin : 20,
|
minBorderMargin : 20,
|
||||||
|
@ -444,13 +613,9 @@ html,body,#map {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.yaxisLabel = $("<div class='axisLabel yaxisLabel'></div>")
|
this.yaxisLabel = $("<div class='axisLabel yaxisLabel'></div>").text("Altitude (ft)").appendTo(container);
|
||||||
.text("Altitude (ft)")
|
|
||||||
.appendTo(container);
|
|
||||||
|
|
||||||
this.xaxisLabel = $("<div class='axisLabel xaxisLabel'></div>")
|
this.xaxisLabel = $("<div class='axisLabel xaxisLabel'></div>").text("Simulation time (s)").appendTo(container);
|
||||||
.text("Simulation time (s)")
|
|
||||||
.appendTo(container);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
altitudePlotter.addTo(map);
|
altitudePlotter.addTo(map);
|
||||||
|
@ -504,6 +669,7 @@ html,body,#map {
|
||||||
};
|
};
|
||||||
|
|
||||||
navdbLayer.update();
|
navdbLayer.update();
|
||||||
|
aiLayer.update();
|
||||||
|
|
||||||
var latlng;
|
var latlng;
|
||||||
var i = 0;
|
var i = 0;
|
||||||
|
|
Loading…
Add table
Reference in a new issue