Implementing separate aircraft markers.

This commit is contained in:
Marat Fayzullin 2023-09-23 12:22:14 -04:00
parent 33d84c3438
commit 4dea8c11ab
6 changed files with 265 additions and 45 deletions

BIN
htdocs/gfx/adsb-72.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

View File

@ -78,6 +78,25 @@ GAprsMarker.prototype.place = function() {
}
};
//
// GoogleMaps-Specific AircraftMarker
//
function GAircraftMarker() { $.extend(this, new AircraftMarker()); }
GAircraftMarker.prototype = new GMarker();
GAircraftMarker.prototype.place = function() {
// Project location and place symbol
var div = this.div;
if (div) {
var point = this.getProjection().fromLatLngToDivPixel(this.position);
if (point) {
div.style.left = point.x - 36 + 'px';
div.style.top = point.y - 36 + 'px';
}
}
};
//
// GoogleMaps-Specific SimpleMarker
//

View File

@ -66,6 +66,9 @@ function LFeatureMarker() { $.extend(this, new LMarker(), new FeatureMarker());
// Leaflet-Specific AprsMarker
function LAprsMarker () { $.extend(this, new LMarker(), new AprsMarker()); }
// Leaflet-Specific AircraftMarker
function LAircraftMarker () { $.extend(this, new LMarker(), new AircraftMarker()); }
//
// Leaflet-Specific Locator
//

View File

@ -390,8 +390,7 @@ FeatureMarker.prototype.getInfoHTML = function(name, receiverMarker = null) {
//
// APRS Marker
// Represents APRS transmitters, as well as AIS (vessels)
// and HFDL (planes).
// Represents APRS transmitters, as well as AIS (vessels).
// Derived classes have to implement:
// setMarkerOpacity()
//
@ -700,3 +699,198 @@ AprsMarker.prototype.getInfoHTML = function(name, receiverMarker = null) {
+ commentString + weatherString + detailsString
+ messageString + hopsString;
};
//
// Aircraft Marker
// Represents aircraft reported by various aeronautic services,
// such as HFDL, ACARS, VDL2, and ADSB.
// Derived classes have to implement:
// setMarkerOpacity()
//
function AircraftMarker() {}
AircraftMarker.prototype = new Marker();
AircraftMarker.prototype.update = function(update) {
this.lastseen = update.lastseen;
this.mode = update.mode;
this.comment = update.location.comment;
// HFDL, ACARS, VDL2, ADSB
this.altitude = update.location.altitude;
this.aircraft = update.location.aircraft;
this.destination = update.location.destination;
this.origin = update.location.origin;
this.flight = update.location.flight;
this.icao = update.location.icao;
this.vspeed = update.location.vspeed;
this.squawk = update.location.squawk;
this.rssi = update.location.rssi;
this.msglog = update.location.msglog;
// Implementation-dependent function call
this.setMarkerPosition(update.callsign, update.location.lat, update.location.lon);
// Age locator
this.age(new Date().getTime() - update.lastseen);
};
AircraftMarker.prototype.draw = function() {
var div = this.div;
var overlay = this.overlay;
if (!div || !overlay) return;
if (this.symbol) {
var tableId = this.symbol.table === '/' ? 0 : 1;
div.style.background = 'url(static/gfx/adsb-72.png)';
div.style['background-size'] = '576px 792px';
div.style['background-position-x'] = -this.symbol.x * 72 + 'px';
div.style['background-position-y'] = -this.symbol.y * 72 + 'px';
}
// If aircraft is flying at a significant altitude...
if (this.altitude >= 500) {
// r = elevation, a = rotation, <x,y> = shadow offset
var r = Math.round(this.altitude / 1000);
var a = - Math.PI * (this.course? this.course - 45 : -45) / 180;
var x = r * Math.cos(a);
var y = r * Math.sin(a);
div.style.filter = 'drop-shadow(' + x + 'px ' + y + 'px 0.5px rgba(0,0,0,0.5))';
} else {
div.style.filter = 'none';
}
div.style.transform = 'scale(0.5)';
if (this.course) {
div.style.transform += ' rotate(' + this.course + 'deg)';
}
if (this.symbol && this.symbol.table !== '/' && this.symbol.table !== '\\') {
overlay.style.display = 'block';
overlay.style['background-position-x'] = -this.symbol.x * 72 + 'px';
overlay.style['background-position-y'] = -this.symbol.y * 72 + 'px';
} else {
overlay.style.display = 'none';
}
if (this.opacity) {
div.style.opacity = this.opacity;
} else {
div.style.opacity = null;
}
if (this.place) this.place();
};
AircraftMarker.prototype.create = function() {
var div = this.div = document.createElement('div');
div.style.position = 'absolute';
div.style.cursor = 'pointer';
div.style.width = '72px';
div.style.height = '72px';
var overlay = this.overlay = document.createElement('div');
overlay.style.width = '72px';
overlay.style.height = '72px';
overlay.style.background = 'url(static/gfx/adsb-72.png)';
overlay.style['background-size'] = '576px 792px';
overlay.style.display = 'none';
div.appendChild(overlay);
return div;
};
AircraftMarker.prototype.getAnchorOffset = function() {
return [0, -12];
};
AircraftMarker.prototype.getInfoHTML = function(name, receiverMarker = null) {
var timeString = moment(this.lastseen).fromNow();
var commentString = '';
var detailsString = '';
var messageString = '';
var distance = '';
if (this.comment) {
commentString += '<div>' + Marker.makeListTitle('Comment') + '<div>' +
this.comment + '</div></div>';
}
if (this.msglog) {
messageString += '<div>' + Marker.makeListTitle('Messages') +
'<pre class="openwebrx-map-console">' +
this.msglog.join('\n<hr>') + '</pre></div>';
}
if (this.icao) {
detailsString += Marker.makeListItem('ICAO', Marker.linkify(this.icao, modes_url));
}
if (this.aircraft) {
detailsString += Marker.makeListItem('Aircraft', Marker.linkify(this.aircraft, flight_url));
}
if (this.squawk) {
detailsString += Marker.makeListItem('Squawk', this.squawk);
}
if (this.origin) {
detailsString += Marker.makeListItem('Origin', this.origin);
}
if (this.destination) {
detailsString += Marker.makeListItem('Destination', this.destination);
}
// Combine course and speed if both present
if (this.course && this.speed) {
detailsString += Marker.makeListItem('Course',
Marker.degToCompass(this.course) + ' ' +
this.speed.toFixed(1) + ' km/h'
);
} else {
if (this.course) {
detailsString += Marker.makeListItem('Course', Marker.degToCompass(this.course));
}
if (this.speed) {
detailsString += Marker.makeListItem('Speed', this.speed.toFixed(1) + ' km/h');
}
}
// Combine altitude and vertical speed
if (this.altitude) {
var alt = this.altitude.toFixed(0) + ' m';
if (this.vspeed > 0) alt += ' &UpperRightArrow;' + this.vspeed + ' m/m';
else if (this.vspeed < 0) alt += ' &LowerRightArrow;' + (-this.vspeed) + ' m/m';
detailsString += Marker.makeListItem('Altitude', alt);
}
if (this.rssi) {
detailsString += Marker.makeListItem('RSSI', this.rssi + ' dB');
}
if (detailsString.length > 0) {
detailsString = '<div>' + Marker.makeListTitle('Details') + detailsString + '</div>';
}
if (receiverMarker) {
distance = ' at ' + Marker.distanceKm(receiverMarker.position, this.position) + ' km';
}
// Linkify title based on what it is (vessel, flight, mode-S code, or else)
var url = modes_url;
if (this.flight) {
name = this.flight;
url = this.flight.match(/^[A-Z]{3}[0-9]+[A-Z]*$/)? flight_url : null;
} else if (this.aircraft) {
name = this.aircraft;
url = flight_url;
}
return '<h3>' + Marker.linkify(name, url) + distance + '</h3>'
+ '<div align="center">' + timeString + ' using ' + this.mode + '</div>'
+ commentString + detailsString + messageString;
};

View File

@ -160,13 +160,24 @@ MapManager.prototype.processUpdates = function(updates) {
case 'latlon':
var marker = self.mman.find(update.callsign);
var markerClass = GSimpleMarker;
var aprsOptions = {}
var options = {}
switch(update.mode) {
case 'HFDL': case 'VDL2': case 'ADSB': case 'ACARS':
markerClass = GAircraftMarker;
break;
case 'APRS': case 'AIS':
markerClass = GAprsMarker;
break;
}
options.color = update.location.color?
update.location.color : self.mman.getColor(update.mode);
if (update.location.symbol) {
markerClass = GAprsMarker;
aprsOptions.symbol = update.location.symbol;
aprsOptions.course = update.location.course;
aprsOptions.speed = update.location.speed;
options.symbol = update.location.symbol;
options.course = update.location.course;
options.speed = update.location.speed;
}
// If new item, create a new marker for it
@ -188,7 +199,7 @@ MapManager.prototype.processUpdates = function(updates) {
marker.setMap(self.mman.isEnabled(update.mode)? map : undefined);
// Apply marker options
marker.setMarkerOptions(aprsOptions);
marker.setMarkerOptions(options);
if (expectedCallsign && expectedCallsign == update.callsign) {
map.panTo(marker.position);
@ -206,16 +217,10 @@ MapManager.prototype.processUpdates = function(updates) {
var options = {}
// If no symbol or color supplied, use defaults by type
if (update.location.symbol) {
options.symbol = update.location.symbol;
} else {
options.symbol = self.mman.getSymbol(update.mode);
}
if (update.location.color) {
options.color = update.location.color;
} else {
options.color = self.mman.getColor(update.mode);
}
options.symbol = update.location.symbol?
update.location.symbol : self.mman.getSymbol(update.mode);
options.color = update.location.color?
update.location.color : self.mman.getColor(update.mode);
// If new item, create a new marker for it
if (!marker) {

View File

@ -39,30 +39,30 @@ MODE_S_FORMATS = [
# Aircraft categories
#
ADSB_CATEGORIES = {
"A0": ("^", "/"), # No ADS-B emitter category information
"A1": ("'", "/"), # Light (< 15500 lbs)
"A2": ("'", "/"), # Small (15500 to 75000 lbs)
"A3": ("^", "/"), # Large (75000 to 300000 lbs)
"A4": ("^", "/"), # High vortex large (aircraft such as B-757)
"A5": ("^", "/"), # Heavy (> 300000 lbs)
"A6": ("^", "/"), # High performance (> 5g acceleration and 400 kts)
"A7": ("X", "/"), # Rotorcraft, regardless of weight
"B0": ("^", "/"), # No ADS-B emitter category information
"B1": ("g", "/"), # Glider or sailplane, regardless of weight
"B2": ("O", "/"), # Airship or balloon, regardless of weight
"B3": ("g", "/"), # Parachutist / skydiver
"B4": ("g", "/"), # Ultralight / hang-glider / paraglider
"B5": ("^", "/"), # Reserved
"B6": ("S", "\\"), # Unmanned aerial vehicle, regardless of weight
"B7": ("S", "/"), # Space / trans-atmospheric vehicle
"C0": ("D", "/"), # No ADS-B emitter category information
"C1": ("f", "/"), # Surface vehicle emergency vehicle
"C2": ("u", "\\"), # Surface vehicle service vehicle
"C3": ("D", "/"), # Point obstacle (includes tethered balloons)
"C4": ("D", "/"), # Cluster obstacle
"C5": ("D", "/"), # Line obstacle
"C6": ("D", "/"), # Reserved
"C7": ("D", "/"), # Reserved
"A0": (0, 0), # No ADS-B emitter category information
"A1": (3, 0), # Light (< 15500 lbs)
"A2": (5, 0), # Small (15500 to 75000 lbs)
"A3": (4, 0), # Large (75000 to 300000 lbs)
"A4": (0, 0), # High vortex large (aircraft such as B-757)
"A5": (1, 7), # Heavy (> 300000 lbs)
"A6": (7, 0), # High performance (> 5g acceleration and 400 kts)
"A7": (6, 5), # Rotorcraft, regardless of weight
"B0": (0, 0), # No ADS-B emitter category information
"B1": (1, 6), # Glider or sailplane, regardless of weight
"B2": (2, 0), # Airship or balloon, regardless of weight
"B3": (10, 0), # Parachutist / skydiver
"B4": (10, 0), # Ultralight / hang-glider / paraglider
"B5": (0, 0), # Reserved
"B6": (4, 3), # Unmanned aerial vehicle, regardless of weight
"B7": (4, 5), # Space / trans-atmospheric vehicle
"C0": (4, 8), # No ADS-B emitter category information
"C1": (2, 8), # Surface vehicle emergency vehicle
"C2": (3, 8), # Surface vehicle service vehicle
"C3": (5, 8), # Point obstacle (includes tethered balloons)
"C4": (6, 9), # Cluster obstacle
"C5": (2, 8), # Line obstacle
"C6": (2, 8), # Reserved
"C7": (2, 8), # Reserved
}
@ -83,11 +83,10 @@ class AircraftLocation(LatLngLocation):
if "category" in self.data and self.data["category"] in ADSB_CATEGORIES:
# Add APRS-like symbol by aircraft category
cat = ADSB_CATEGORIES[self.data["category"]]
res["symbol"] = getSymbolData(cat[0], cat[1])
res["symbol"] = { "x": cat[0], "y": cat[1] }
else:
# Add APRS-like aircraft symbol (red or blue, depending on mode)
mod = '/' if self.data["mode"]=="ADSB" else '\\'
res["symbol"] = getSymbolData('^', mod)
res["symbol"] = { "x": 0, "y": 0 }
# Convert aircraft-specific data into APRS-like data
for x in ["icao", "aircraft", "flight", "speed", "altitude", "course", "destination", "origin", "vspeed", "squawk", "rssi", "msglog"]:
if x in self.data: