2020-01-03 21:16:40 +01:00
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
function PlaneObject(icao) {
|
|
|
|
|
// Info about the plane
|
|
|
|
|
this.icao = icao;
|
|
|
|
|
this.icaorange = findICAORange(icao);
|
|
|
|
|
this.flight = null;
|
|
|
|
|
this.squawk = null;
|
|
|
|
|
this.selected = false;
|
|
|
|
|
this.category = null;
|
2020-04-21 03:06:33 +02:00
|
|
|
this.dataSource = "mode_s";
|
2020-01-03 21:16:40 +01:00
|
|
|
|
|
|
|
|
this.trCache = [];
|
|
|
|
|
|
|
|
|
|
// Basic location information
|
|
|
|
|
this.altitude = null;
|
|
|
|
|
this.alt_baro = null;
|
|
|
|
|
this.alt_geom = null;
|
|
|
|
|
this.altitudeTime = 0;
|
|
|
|
|
this.bad_alt = null;
|
|
|
|
|
this.bad_altTime = null;
|
|
|
|
|
this.alt_reliable = 0;
|
|
|
|
|
|
|
|
|
|
this.speed = null;
|
|
|
|
|
this.gs = null;
|
|
|
|
|
this.ias = null;
|
|
|
|
|
this.tas = null;
|
|
|
|
|
|
|
|
|
|
this.track = null;
|
|
|
|
|
this.track_rate = null;
|
|
|
|
|
this.mag_heading = null;
|
|
|
|
|
this.true_heading = null;
|
|
|
|
|
this.mach = null;
|
|
|
|
|
this.roll = null;
|
|
|
|
|
this.nav_altitude = null;
|
|
|
|
|
this.nav_heading = null;
|
|
|
|
|
this.nav_modes = null;
|
|
|
|
|
this.nav_qnh = null;
|
|
|
|
|
this.rc = null;
|
|
|
|
|
|
|
|
|
|
this.rotation = 0;
|
|
|
|
|
|
|
|
|
|
this.nac_p = null;
|
|
|
|
|
this.nac_v = null;
|
|
|
|
|
this.nic_baro = null;
|
|
|
|
|
this.sil_type = null;
|
|
|
|
|
this.sil = null;
|
|
|
|
|
|
|
|
|
|
this.baro_rate = null;
|
|
|
|
|
this.geom_rate = null;
|
|
|
|
|
this.vert_rate = null;
|
|
|
|
|
|
2020-04-22 08:53:27 +02:00
|
|
|
|
|
|
|
|
this.wd = null;
|
|
|
|
|
this.ws = null;
|
|
|
|
|
this.oat = null;
|
|
|
|
|
this.tat = null;
|
|
|
|
|
|
2020-01-03 21:16:40 +01:00
|
|
|
this.version = null;
|
|
|
|
|
|
|
|
|
|
this.prev_position = null;
|
|
|
|
|
this.prev_time = null;
|
|
|
|
|
this.prev_track = null;
|
|
|
|
|
this.position = null;
|
|
|
|
|
this.sitedist = null;
|
|
|
|
|
this.too_fast = 0;
|
|
|
|
|
|
|
|
|
|
// Data packet numbers
|
|
|
|
|
this.messages = 0;
|
|
|
|
|
this.rssi = null;
|
|
|
|
|
this.msgs1090 = 0;
|
|
|
|
|
this.msgs978 = 0;
|
|
|
|
|
this.messageRate = 0;
|
|
|
|
|
this.messageRateOld = 0;
|
|
|
|
|
|
|
|
|
|
// Track history as a series of line segments
|
|
|
|
|
this.elastic_feature = null;
|
|
|
|
|
this.track_linesegs = [];
|
|
|
|
|
this.history_size = 0;
|
|
|
|
|
this.trace = []; // save last 30 seconds of positions
|
|
|
|
|
|
|
|
|
|
// Track (direction) at the time we last appended to the track history
|
|
|
|
|
this.tail_track = null;
|
|
|
|
|
this.tail_true = null;
|
|
|
|
|
// Timestamp of the most recent point appended to the track history
|
|
|
|
|
this.tail_update = null;
|
|
|
|
|
|
|
|
|
|
// When was this last updated (receiver timestamp)
|
|
|
|
|
this.last_message_time = 0;
|
|
|
|
|
this.position_time = 0;
|
|
|
|
|
|
2020-01-14 10:27:36 +01:00
|
|
|
this.last = 0; // last json this plane was included in
|
|
|
|
|
|
2020-01-03 21:16:40 +01:00
|
|
|
// When was this last updated (seconds before last update)
|
|
|
|
|
this.seen = null;
|
|
|
|
|
this.seen_pos = null;
|
|
|
|
|
|
|
|
|
|
// Display info
|
|
|
|
|
this.visible = true;
|
|
|
|
|
this.marker = null;
|
|
|
|
|
this.markerStyle = null;
|
|
|
|
|
this.markerIcon = null;
|
|
|
|
|
this.markerStyleKey = null;
|
|
|
|
|
this.markerSvgKey = null;
|
|
|
|
|
this.baseScale = 1;
|
|
|
|
|
|
|
|
|
|
// start from a computed registration, let the DB override it
|
|
|
|
|
// if it has something else.
|
|
|
|
|
this.registration = registration_from_hexid(this.icao);
|
|
|
|
|
this.icaoType = null;
|
|
|
|
|
this.typeDescription = null;
|
2020-08-12 17:08:42 +02:00
|
|
|
this.typeLong = null;
|
2020-01-03 21:16:40 +01:00
|
|
|
this.wtc = null;
|
|
|
|
|
|
2020-03-16 16:21:11 +01:00
|
|
|
|
2020-10-22 19:56:09 +02:00
|
|
|
this.regLoaded = false;
|
2020-03-16 16:21:11 +01:00
|
|
|
// request metadata
|
2020-10-22 19:56:09 +02:00
|
|
|
if (!dbServer && !this.regLoaded)
|
2020-10-20 21:31:42 +02:00
|
|
|
this.getAircraftData();
|
2020-03-16 16:21:11 +01:00
|
|
|
|
2020-10-20 21:31:42 +02:00
|
|
|
// military icao ranges
|
|
|
|
|
if (this.milRange()) {
|
|
|
|
|
this.military = true;
|
|
|
|
|
}
|
2020-03-16 16:21:11 +01:00
|
|
|
}
|
2020-03-18 10:28:23 +01:00
|
|
|
PlaneObject.prototype.checkLayers = function() {
|
|
|
|
|
if (!this.trail_features)
|
|
|
|
|
this.createFeatures();
|
2020-03-23 10:17:43 +01:00
|
|
|
if ((showTrace || trackLabels) && !this.trail_labels)
|
2020-03-18 10:28:23 +01:00
|
|
|
this.createLabels();
|
|
|
|
|
}
|
2020-03-16 16:21:11 +01:00
|
|
|
|
2020-03-18 10:28:23 +01:00
|
|
|
PlaneObject.prototype.createFeatures = function() {
|
|
|
|
|
this.trail_features = new ol.source.Vector();
|
2020-01-03 21:16:40 +01:00
|
|
|
|
|
|
|
|
this.layer = new ol.layer.Vector({
|
|
|
|
|
name: this.icao,
|
|
|
|
|
isTrail: true,
|
2020-03-18 10:28:23 +01:00
|
|
|
source: this.trail_features,
|
2020-01-03 21:16:40 +01:00
|
|
|
declutter: false,
|
2020-03-03 09:59:05 +01:00
|
|
|
zIndex: 150,
|
2020-01-03 21:16:40 +01:00
|
|
|
});
|
|
|
|
|
|
2020-03-18 10:28:23 +01:00
|
|
|
trailGroup.push(this.layer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PlaneObject.prototype.createLabels = function() {
|
|
|
|
|
this.trail_labels = new ol.source.Vector();
|
|
|
|
|
|
2020-01-03 21:16:40 +01:00
|
|
|
this.layer_labels = new ol.layer.Vector({
|
|
|
|
|
name: this.icao + '_labels',
|
|
|
|
|
isTrail: true,
|
2020-03-18 10:28:23 +01:00
|
|
|
source: this.trail_labels,
|
2020-01-03 21:16:40 +01:00
|
|
|
declutter: true,
|
2020-03-03 09:59:05 +01:00
|
|
|
zIndex: 151,
|
2020-01-03 21:16:40 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
trailGroup.push(this.layer_labels);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PlaneObject.prototype.logSel = function(loggable) {
|
|
|
|
|
if (debugTracks && this.selected && !SelectedAllPlanes)
|
|
|
|
|
console.log(loggable);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PlaneObject.prototype.isFiltered = function() {
|
2020-04-02 16:13:33 +02:00
|
|
|
if (this.selected && !SelectedAllPlanes)
|
|
|
|
|
return false;
|
2020-01-03 21:16:40 +01:00
|
|
|
|
|
|
|
|
if (onlySelected && !this.selected) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-21 03:06:33 +02:00
|
|
|
if (onlyMLAT && !(this.dataSource == "mlat" || (this.dataSource == "mode_s" && this.position == null))) {
|
2020-01-03 21:16:40 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (onlyMilitary && !this.military) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (onlyADSB && this.dataSource != "adsb" && this.dataSource != "uat") {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-26 20:50:20 +02:00
|
|
|
if (onlyDataSource && this.dataSource != onlyDataSource) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-03 21:16:40 +01:00
|
|
|
if (filterTISB && this.dataSource == "tisb") {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-17 10:45:31 +02:00
|
|
|
if (!filterTracks && altFiltered(this.altitude))
|
2020-01-03 21:16:40 +01:00
|
|
|
return true;
|
|
|
|
|
|
2020-05-16 14:03:49 +02:00
|
|
|
if (PlaneFilter.icao && !this.icao.match(PlaneFilter.icao) ) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-16 16:21:11 +01:00
|
|
|
if (PlaneFilter.type && (!this.icaoType || !this.icaoType.match(PlaneFilter.type)) ) {
|
2020-01-04 13:51:14 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-16 16:21:11 +01:00
|
|
|
if (PlaneFilter.description && (!this.typeDescription || !this.typeDescription.match(PlaneFilter.description)) ) {
|
2020-01-14 22:00:43 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-16 16:21:11 +01:00
|
|
|
if (PlaneFilter.callsign
|
|
|
|
|
&& (!this.flight || !this.flight.match(PlaneFilter.callsign))
|
|
|
|
|
&& (!this.squawk || !this.squawk.match(PlaneFilter.callsign))
|
2020-01-05 22:54:54 +01:00
|
|
|
) {
|
2020-01-04 13:51:14 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-03 21:16:40 +01:00
|
|
|
// filter out ground vehicles
|
2020-05-19 17:29:38 +02:00
|
|
|
if (PlaneFilter.groundVehicles == 'filtered') {
|
|
|
|
|
if (typeof this.category === 'string' && this.category.startsWith('C'))
|
|
|
|
|
return true;
|
|
|
|
|
if (this.altitude == 'ground' && (this.addrtype == 'adsb_icao_nt' || this.addrtype == 'tisb_other'))
|
2020-01-03 21:16:40 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// filter out blocked MLAT flights
|
2020-05-19 17:29:38 +02:00
|
|
|
if (PlaneFilter.blockedMLAT == 'filtered') {
|
|
|
|
|
if (typeof this.icao === 'string' && this.icao.startsWith('~'))
|
2020-01-03 21:16:40 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-23 20:30:06 +02:00
|
|
|
if (this.sitedist && this.sitedist > filterMaxRange)
|
|
|
|
|
return true;
|
|
|
|
|
|
2020-01-03 21:16:40 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-05-17 10:45:31 +02:00
|
|
|
function altFiltered(altitude) {
|
2020-03-16 16:21:11 +01:00
|
|
|
if (PlaneFilter.minAltitude == null || PlaneFilter.maxAltitude == null)
|
2020-01-03 21:16:40 +01:00
|
|
|
return false;
|
|
|
|
|
if (altitude == null) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
const planeAltitude = altitude === "ground" ? 0 : altitude;
|
2020-03-16 16:21:11 +01:00
|
|
|
if (planeAltitude < PlaneFilter.minAltitude || planeAltitude > PlaneFilter.maxAltitude) {
|
2020-01-03 21:16:40 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PlaneObject.prototype.updateTail = function() {
|
|
|
|
|
|
|
|
|
|
this.tail_update = this.prev_time;
|
|
|
|
|
this.tail_track = this.prev_track;
|
|
|
|
|
this.tail_rot = this.prev_rot;
|
|
|
|
|
this.tail_true = this.prev_true;
|
|
|
|
|
this.tail_position = this.prev_position;
|
|
|
|
|
|
|
|
|
|
return this.updateTrackPrev();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PlaneObject.prototype.updateTrackPrev = function() {
|
|
|
|
|
|
|
|
|
|
this.prev_position = this.position;
|
|
|
|
|
this.prev_time = this.position_time;
|
|
|
|
|
this.prev_track = this.track;
|
|
|
|
|
this.prev_rot = this.rotation;
|
|
|
|
|
this.prev_true = this.true_head;
|
|
|
|
|
this.prev_alt = this.altitude;
|
|
|
|
|
this.prev_alt_rounded = this.alt_rounded;
|
|
|
|
|
this.prev_speed = this.speed;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Appends data to the running track so we can get a visual tail on the plane
|
|
|
|
|
// Only useful for a long running browser session.
|
2020-03-22 20:54:48 +01:00
|
|
|
PlaneObject.prototype.updateTrack = function(now, last, serverTrack, stale) {
|
2020-01-03 21:16:40 +01:00
|
|
|
if (this.position == null)
|
|
|
|
|
return false;
|
2020-03-22 23:14:14 +01:00
|
|
|
if (this.prev_position && this.position[0] == this.prev_position[0] && this.position[1] == this.prev_position[1]
|
|
|
|
|
&& !serverTrack)
|
2020-01-03 21:16:40 +01:00
|
|
|
return false;
|
|
|
|
|
if (this.bad_position && this.position[0] == this.bad_position[0] && this.position[1] == this.bad_position[1])
|
|
|
|
|
return false;
|
|
|
|
|
|
2020-05-13 19:45:24 +02:00
|
|
|
if (this.position[0] > 180 || this.position[0] < -180 || this.position[1] > 90 || this.position[1] < -90) {
|
|
|
|
|
console.log("Ignoring Impossible Position for " + this.icao + ": " + this.position);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-02 23:34:09 +01:00
|
|
|
if (this.position && SitePosition) {
|
|
|
|
|
this.sitedist = ol.sphere.getDistance(SitePosition, this.position);
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-27 08:45:56 +01:00
|
|
|
let projHere = ol.proj.fromLonLat(this.position);
|
|
|
|
|
let on_ground = (this.altitude === "ground");
|
2020-01-03 21:16:40 +01:00
|
|
|
|
2020-03-27 08:45:56 +01:00
|
|
|
let is_leg = false;
|
2020-03-22 20:35:16 +01:00
|
|
|
if (this.leg_ts == now)
|
|
|
|
|
is_leg = 'end';
|
|
|
|
|
if (this.leg_ts == last)
|
|
|
|
|
is_leg = 'start';
|
|
|
|
|
|
2020-01-03 21:16:40 +01:00
|
|
|
if (this.track_linesegs.length == 0) {
|
|
|
|
|
// Brand new track
|
|
|
|
|
//console.log(this.icao + " new track");
|
2020-03-22 20:54:48 +01:00
|
|
|
if (this.leg_ts == now)
|
|
|
|
|
is_leg = 'start';
|
2020-03-27 08:45:56 +01:00
|
|
|
let newseg = { fixed: new ol.geom.LineString([projHere]),
|
2020-01-03 21:16:40 +01:00
|
|
|
feature: null,
|
2020-03-22 20:54:48 +01:00
|
|
|
estimated: false,
|
2020-01-03 21:16:40 +01:00
|
|
|
ground: on_ground,
|
|
|
|
|
altitude: this.alt_rounded,
|
|
|
|
|
alt_real: this.altitude,
|
|
|
|
|
speed: this.speed,
|
|
|
|
|
ts: now,
|
2020-01-14 15:02:04 +01:00
|
|
|
track: this.rotation,
|
2020-03-22 20:35:16 +01:00
|
|
|
leg: is_leg,
|
2020-01-03 21:16:40 +01:00
|
|
|
};
|
|
|
|
|
this.track_linesegs.push(newseg);
|
|
|
|
|
this.history_size ++;
|
|
|
|
|
this.updateTrackPrev();
|
|
|
|
|
return this.updateTail();
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-27 08:45:56 +01:00
|
|
|
let projPrev = ol.proj.fromLonLat(this.prev_position);
|
|
|
|
|
let lastseg = this.track_linesegs[this.track_linesegs.length - 1];
|
2020-01-03 21:16:40 +01:00
|
|
|
|
2020-04-23 09:57:31 +02:00
|
|
|
let distance = ol.sphere.getDistance(this.position, this.prev_position);
|
2020-04-23 11:07:21 +02:00
|
|
|
let elapsed = this.position_time - this.prev_time;
|
2020-04-23 09:57:31 +02:00
|
|
|
|
2020-03-27 08:45:56 +01:00
|
|
|
let derivedMach = 0.01;
|
|
|
|
|
let filterSpeed = 10000;
|
2020-03-02 23:34:09 +01:00
|
|
|
|
2020-03-19 14:23:58 +01:00
|
|
|
const pFilter = (positionFilter == true || (positionFilter == 'onlyMLAT' && this.dataSource == "mlat"));
|
|
|
|
|
|
|
|
|
|
if (pFilter) {
|
2020-03-02 23:34:09 +01:00
|
|
|
derivedMach = (distance/(this.position_time - this.prev_time + 0.4))/343;
|
|
|
|
|
filterSpeed = on_ground ? positionFilterSpeed/10 : positionFilterSpeed;
|
|
|
|
|
filterSpeed = (this.speed != null && this.prev_speed != null) ? (positionFilterGsFactor*(Math.max(this.speed, this.prev_speed)+10+(this.dataSource == "mlat")*100)/666) : filterSpeed;
|
|
|
|
|
}
|
2020-01-03 21:16:40 +01:00
|
|
|
|
|
|
|
|
// ignore the position if the object moves faster than positionFilterSpeed (default Mach 3.5)
|
|
|
|
|
// or faster than twice the transmitted groundspeed
|
2020-03-19 14:23:58 +01:00
|
|
|
if (pFilter && derivedMach > filterSpeed && this.too_fast < 1) {
|
2020-01-03 21:16:40 +01:00
|
|
|
this.bad_position = this.position;
|
|
|
|
|
this.too_fast++;
|
|
|
|
|
if (debugPosFilter) {
|
|
|
|
|
console.log(this.icao + " / " + this.name + " ("+ this.dataSource + "): Implausible position filtered: " + this.bad_position[0] + ", " + this.bad_position[1] + " (kts/Mach " + (derivedMach*666).toFixed(0) + " > " + (filterSpeed*666).toFixed(0) + " / " + derivedMach.toFixed(2) + " > " + filterSpeed.toFixed(2) + ") (" + (this.position_time - this.prev_time + 0.2).toFixed(1) + "s)");
|
|
|
|
|
}
|
|
|
|
|
this.position = this.prev_position;
|
|
|
|
|
this.position_time = this.prev_time;
|
|
|
|
|
if (debugPosFilter) {
|
|
|
|
|
this.drawRedDot(this.bad_position);
|
|
|
|
|
jumpTo = this.icao;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
} else {
|
|
|
|
|
this.too_fast = Math.max(-5, this.too_fast-0.8);
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-07 05:50:44 +01:00
|
|
|
if (this.request_rotation_from_track && this.prev_position) {
|
2020-01-03 21:16:40 +01:00
|
|
|
this.rotation = bearingFromLonLat(this.prev_position, this.position);
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-04 18:48:06 +01:00
|
|
|
|
|
|
|
|
// special case crossing the 180 -180 longitude line by just starting a new track
|
|
|
|
|
if ((this.position[0] < -90 && this.prev_position[0] > 90)
|
|
|
|
|
|| (this.position[0] > 90 && this.prev_position[0] < -90)
|
|
|
|
|
) {
|
2020-04-23 09:57:31 +02:00
|
|
|
lastseg.fixed.appendCoordinate(projPrev);
|
2020-01-04 18:48:06 +01:00
|
|
|
|
2020-04-23 09:57:31 +02:00
|
|
|
this.cross180(on_ground, is_leg);
|
2020-01-04 18:48:06 +01:00
|
|
|
|
2020-04-23 09:57:31 +02:00
|
|
|
this.history_size += 258;
|
2020-01-04 18:48:06 +01:00
|
|
|
|
|
|
|
|
return this.updateTail();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2020-01-03 21:16:40 +01:00
|
|
|
// Determine if track data are intermittent/stale
|
|
|
|
|
// Time difference between two position updates should not be much
|
|
|
|
|
// greater than the difference between data inputs
|
2020-05-07 21:03:35 +02:00
|
|
|
let time_difference = (this.position_time - this.prev_time) - 2;
|
|
|
|
|
if (!loadFinished || serverTrack)
|
|
|
|
|
time_difference = (this.position_time - this.prev_time) - (now - last);
|
2020-01-03 21:16:40 +01:00
|
|
|
|
2020-03-27 08:45:56 +01:00
|
|
|
//let stale_timeout = lastseg.estimated ? 5 : 10;
|
|
|
|
|
let stale_timeout = 15;
|
2020-01-03 21:16:40 +01:00
|
|
|
|
|
|
|
|
// MLAT data are given some more leeway
|
|
|
|
|
if (this.dataSource == "mlat")
|
|
|
|
|
stale_timeout = 15;
|
|
|
|
|
|
|
|
|
|
// On the ground you can't go that quick
|
|
|
|
|
if (on_ground)
|
|
|
|
|
stale_timeout = 30;
|
|
|
|
|
|
|
|
|
|
// Also check if the position was already stale when it was exported by dump1090
|
|
|
|
|
// Makes stale check more accurate for example for 30s spaced history points
|
|
|
|
|
|
2020-05-08 10:24:56 +02:00
|
|
|
let estimated = (time_difference > stale_timeout) || ((now - this.position_time) > stale_timeout) || stale;
|
2020-01-03 21:16:40 +01:00
|
|
|
|
|
|
|
|
/*
|
2020-03-27 08:45:56 +01:00
|
|
|
let track_change = this.track != null ? Math.abs(this.tail_track - this.track) : NaN;
|
2020-01-03 21:16:40 +01:00
|
|
|
track_change = track_change < 180 ? track_change : Math.abs(track_change - 360);
|
2020-03-27 08:45:56 +01:00
|
|
|
let true_change = this.trueheading != null ? Math.abs(this.tail_true - this.true_heading) : NaN;
|
2020-01-03 21:16:40 +01:00
|
|
|
true_change = true_change < 180 ? true_change : Math.abs(true_change - 360);
|
|
|
|
|
if (!isNaN(true_change)) {
|
|
|
|
|
track_change = isNaN(track_change) ? true_change : Math.max(track_change, true_change);
|
|
|
|
|
}
|
|
|
|
|
*/
|
2020-03-27 08:45:56 +01:00
|
|
|
let track_change = Math.abs(this.tail_rot - this.rotation);
|
2020-01-03 21:16:40 +01:00
|
|
|
|
2020-03-27 08:45:56 +01:00
|
|
|
let alt_change = Math.abs(this.alt_rounded - lastseg.altitude);
|
|
|
|
|
let since_update = this.prev_time - this.tail_update;
|
|
|
|
|
let distance_traveled = ol.sphere.getDistance(this.tail_position, this.prev_position);
|
2020-01-03 21:16:40 +01:00
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
this.prev_alt_rounded !== lastseg.altitude
|
|
|
|
|
|| this.prev_time > lastseg.ts + 300
|
2020-04-30 02:45:27 +02:00
|
|
|
|| (!noVanish && this.prev_time > lastseg.ts + 60)
|
2020-01-05 20:34:25 +01:00
|
|
|
|| estimated != lastseg.estimated
|
2020-01-03 21:16:40 +01:00
|
|
|
|| tempTrails
|
2020-01-12 21:51:38 +01:00
|
|
|
|| debugAll ||
|
|
|
|
|
(
|
2020-04-30 02:45:27 +02:00
|
|
|
serverTrack && !noVanish &&
|
2020-01-12 21:51:38 +01:00
|
|
|
(
|
2020-03-21 02:42:46 +01:00
|
|
|
this.prev_time - lastseg.ts > 5
|
|
|
|
|
|| estimated
|
2020-01-12 21:51:38 +01:00
|
|
|
|| track_change > 2
|
|
|
|
|
|| Math.abs(this.prev_speed - lastseg.speed) > 5
|
|
|
|
|
|| Math.abs(this.prev_alt - lastseg.alt_real) > 50
|
2020-03-22 13:50:29 +01:00
|
|
|
|| is_leg
|
2020-01-12 21:51:38 +01:00
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
|
2020-01-03 21:16:40 +01:00
|
|
|
//lastseg.ground != on_ground
|
|
|
|
|
//|| (!on_ground && isNaN(alt_change))
|
|
|
|
|
//|| (alt_change > 700)
|
|
|
|
|
//|| (alt_change > 375 && this.alt_rounded < 9000)
|
|
|
|
|
//|| (alt_change > 150 && this.alt_rounded < 5500)
|
|
|
|
|
) {
|
|
|
|
|
// Create a new segment as the ground state or the altitude changed.
|
|
|
|
|
// The new state is only drawn after the state has changed
|
|
|
|
|
// and we then get a new position.
|
|
|
|
|
|
2020-05-09 10:05:50 +02:00
|
|
|
this.logSel("sec_elapsed: " + since_update.toFixed(1) + " alt_change: "+ alt_change.toFixed(0) + " derived_speed(kts/Mach): " + (distance_traveled/since_update*1.94384).toFixed(0) + " / " + (distance_traveled/since_update/343).toFixed(1) + " dist:" + distance_traveled.toFixed(0));
|
2020-01-03 21:16:40 +01:00
|
|
|
|
2020-04-23 09:57:31 +02:00
|
|
|
let points = [projPrev];
|
2020-04-23 11:07:21 +02:00
|
|
|
|
|
|
|
|
if (since_update > 3600 && distance_traveled / since_update * 3.6 < 100) {
|
|
|
|
|
// don't draw a line if a long time has elapsed but no great distance was traveled
|
|
|
|
|
} else {
|
|
|
|
|
lastseg.fixed.appendCoordinate(projPrev);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// draw great circle path for long distances
|
2020-05-08 10:24:56 +02:00
|
|
|
if (distance > 30000
|
2020-04-23 11:07:21 +02:00
|
|
|
&& !(elapsed > 3600 && distance / elapsed * 3.6 < 100)
|
|
|
|
|
// don't draw a line if a long time has elapsed but no great distance was traveled
|
|
|
|
|
) {
|
2020-05-08 10:24:56 +02:00
|
|
|
estimated = true;
|
2020-04-23 09:57:31 +02:00
|
|
|
let nPoints = distance / 19000;
|
|
|
|
|
let greyskull = Math.ceil(Math.log(nPoints) / Math.log(2));
|
|
|
|
|
//console.log(Math.round(nPoints) + ' ' + greyskull);
|
|
|
|
|
points = makeCircle([this.prev_position, this.position], greyskull);
|
|
|
|
|
for (let i in points)
|
|
|
|
|
points[i] = ol.proj.fromLonLat(points[i]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.track_linesegs.push({ fixed: new ol.geom.LineString(points),
|
2020-01-03 21:16:40 +01:00
|
|
|
feature: null,
|
2020-01-05 20:34:25 +01:00
|
|
|
estimated: estimated,
|
2020-01-03 21:16:40 +01:00
|
|
|
altitude: this.prev_alt_rounded,
|
|
|
|
|
alt_real: this.prev_alt,
|
|
|
|
|
speed: this.prev_speed,
|
|
|
|
|
ground: on_ground,
|
|
|
|
|
ts: this.prev_time,
|
2020-01-14 15:02:04 +01:00
|
|
|
track: this.prev_rot,
|
2020-03-22 13:50:29 +01:00
|
|
|
leg: is_leg,
|
2020-01-03 21:16:40 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.history_size += 2;
|
|
|
|
|
|
|
|
|
|
return this.updateTail();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Add current position to the existing track.
|
|
|
|
|
// We only retain some points depending on time elapsed and track change
|
2020-03-27 08:45:56 +01:00
|
|
|
let turn_density = 6.5;
|
2020-09-06 16:05:05 +02:00
|
|
|
if (pTracks) turn_density = 3;
|
2020-01-03 21:16:40 +01:00
|
|
|
if (
|
2020-09-06 16:05:05 +02:00
|
|
|
since_update > 86 + !!pTracks * 90 ||
|
2020-01-03 21:16:40 +01:00
|
|
|
(!on_ground && since_update > (100/turn_density)/track_change) ||
|
2020-09-06 16:05:05 +02:00
|
|
|
(!on_ground && isNaN(track_change) && since_update > 8 + !!pTracks * 22) ||
|
2020-01-03 21:16:40 +01:00
|
|
|
(on_ground && since_update > (120/turn_density)/track_change && distance_traveled > 20) ||
|
|
|
|
|
(on_ground && distance_traveled > 50 && since_update > 5) ||
|
|
|
|
|
debugAll
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
|
|
lastseg.fixed.appendCoordinate(projPrev);
|
|
|
|
|
this.history_size ++;
|
|
|
|
|
|
|
|
|
|
this.logSel("sec_elapsed: " + since_update.toFixed(1) + " " + (on_ground ? "ground" : "air") + " dist:" + distance_traveled.toFixed(0) + " track_change: "+ track_change.toFixed(1) + " derived_speed(kts/Mach): " + (distance_traveled/since_update*1.94384).toFixed(0) + " / " + (distance_traveled/since_update/343).toFixed(1));
|
|
|
|
|
|
|
|
|
|
return this.updateTail();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return this.updateTrackPrev();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// This is to remove the line from the screen if we deselect the plane
|
|
|
|
|
PlaneObject.prototype.clearLines = function() {
|
2020-03-16 16:21:11 +01:00
|
|
|
if (this.layer && this.layer.getVisible()) {
|
2020-01-03 21:16:40 +01:00
|
|
|
this.layer.setVisible(false);
|
|
|
|
|
}
|
2020-03-18 10:28:23 +01:00
|
|
|
if (this.layer_labels && this.layer_labels.getVisible())
|
|
|
|
|
this.layer_labels.setVisible(false);
|
2020-01-03 21:16:40 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PlaneObject.prototype.getDataSourceNumber = function() {
|
|
|
|
|
// MLAT
|
2020-04-21 03:39:28 +02:00
|
|
|
if (this.dataSource == "mode_s")
|
2020-01-12 15:13:35 +01:00
|
|
|
return 5;
|
2020-04-21 03:39:28 +02:00
|
|
|
if (this.dataSource == "adsc")
|
|
|
|
|
return 6;
|
2020-01-03 21:16:40 +01:00
|
|
|
if (this.dataSource == "mlat") {
|
|
|
|
|
return 3;
|
|
|
|
|
}
|
2020-05-31 21:50:17 +02:00
|
|
|
if (this.dataSource == "uat" || (this.addrtype && this.addrtype.substring(0,4) == "adsr"))
|
2020-01-03 21:16:40 +01:00
|
|
|
return 2; // UAT
|
|
|
|
|
|
2020-03-27 08:45:56 +01:00
|
|
|
// Not MLAT, but position reported - ADSB or letiants
|
2020-01-03 21:16:40 +01:00
|
|
|
if (this.dataSource == "tisb")
|
|
|
|
|
return 4; // TIS-B
|
|
|
|
|
if (this.dataSource == "adsb")
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
// Otherwise Mode S
|
2020-04-21 03:39:28 +02:00
|
|
|
return 7;
|
2020-01-03 21:16:40 +01:00
|
|
|
|
|
|
|
|
// TODO: add support for Mode A/C
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PlaneObject.prototype.getDataSource = function() {
|
|
|
|
|
// MLAT
|
|
|
|
|
if (this.dataSource == "mlat") {
|
|
|
|
|
return 'mlat';
|
|
|
|
|
}
|
|
|
|
|
if (this.dataSource == "uat" && this.dataSource != "tisb")
|
|
|
|
|
return 'uat';
|
|
|
|
|
|
|
|
|
|
if (this.addrtype) {
|
|
|
|
|
return this.addrtype;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.dataSource == "adsb")
|
|
|
|
|
return "adsb_icao";
|
|
|
|
|
|
|
|
|
|
if (this.dataSource == "tisb")
|
|
|
|
|
return "tisb";
|
|
|
|
|
|
|
|
|
|
// Otherwise Mode S
|
|
|
|
|
return 'mode_s';
|
|
|
|
|
|
|
|
|
|
// TODO: add support for Mode A/C
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PlaneObject.prototype.getMarkerColor = function() {
|
2020-02-29 20:01:51 +01:00
|
|
|
if (monochromeMarkers) {
|
|
|
|
|
return monochromeMarkers;
|
|
|
|
|
}
|
2020-02-29 19:04:08 +01:00
|
|
|
|
2020-04-13 12:38:05 +02:00
|
|
|
let alt = this.alt_rounded;
|
2020-04-17 08:01:01 +02:00
|
|
|
if (this.category == 'C3' || this.icaoType == 'TWR' || (this.icaoType == null && this.squawk == 7777))
|
2020-04-13 12:38:05 +02:00
|
|
|
alt = 'ground';
|
|
|
|
|
|
2020-03-27 08:45:56 +01:00
|
|
|
let h, s, l;
|
2020-01-03 21:16:40 +01:00
|
|
|
|
2020-04-13 12:38:05 +02:00
|
|
|
let colorArr = altitudeColor(alt);
|
2020-01-03 21:16:40 +01:00
|
|
|
|
|
|
|
|
h = colorArr[0];
|
|
|
|
|
s = colorArr[1];
|
|
|
|
|
l = colorArr[2];
|
|
|
|
|
|
|
|
|
|
// If we have not seen a recent position update, change color
|
|
|
|
|
if (this.seen_pos > 15 && !globeIndex) {
|
|
|
|
|
h += ColorByAlt.stale.h;
|
|
|
|
|
s += ColorByAlt.stale.s;
|
|
|
|
|
l += ColorByAlt.stale.l;
|
|
|
|
|
}
|
2020-04-13 12:38:05 +02:00
|
|
|
if (alt == "ground") {
|
2020-01-03 21:16:40 +01:00
|
|
|
l += 15;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If this marker is selected, change color
|
|
|
|
|
if (this.selected && !SelectedAllPlanes && !onlySelected){
|
|
|
|
|
h += ColorByAlt.selected.h;
|
|
|
|
|
s += ColorByAlt.selected.s;
|
|
|
|
|
l += ColorByAlt.selected.l;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If this marker is a mlat position, change color
|
|
|
|
|
if (this.dataSource == "mlat") {
|
|
|
|
|
h += ColorByAlt.mlat.h;
|
|
|
|
|
s += ColorByAlt.mlat.s;
|
|
|
|
|
l += ColorByAlt.mlat.l;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (h < 0) {
|
|
|
|
|
h = (h % 360) + 360;
|
|
|
|
|
} else if (h >= 360) {
|
|
|
|
|
h = h % 360;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-29 19:04:08 +01:00
|
|
|
//if (s < 5) s = 5;
|
|
|
|
|
if (s > 95) s = 95;
|
2020-01-03 21:16:40 +01:00
|
|
|
|
|
|
|
|
if (l < 5) l = 5;
|
|
|
|
|
else if (l > 95) l = 95;
|
|
|
|
|
|
|
|
|
|
return 'hsl(' + h.toFixed(0) + ',' + s.toFixed(0) + '%,' + l.toFixed(0) + '%)'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function altitudeColor(altitude) {
|
2020-03-27 08:45:56 +01:00
|
|
|
let h, s, l;
|
2020-01-03 21:16:40 +01:00
|
|
|
|
|
|
|
|
if (altitude == null) {
|
|
|
|
|
h = ColorByAlt.unknown.h;
|
|
|
|
|
s = ColorByAlt.unknown.s;
|
|
|
|
|
l = ColorByAlt.unknown.l;
|
|
|
|
|
} else if (altitude === "ground") {
|
|
|
|
|
h = ColorByAlt.ground.h;
|
|
|
|
|
s = ColorByAlt.ground.s;
|
|
|
|
|
l = ColorByAlt.ground.l;
|
|
|
|
|
} else {
|
|
|
|
|
s = ColorByAlt.air.s;
|
|
|
|
|
|
|
|
|
|
// find the pair of points the current altitude lies between,
|
|
|
|
|
// and interpolate the hue between those points
|
2020-03-27 08:45:56 +01:00
|
|
|
let hpoints = ColorByAlt.air.h;
|
2020-01-03 21:16:40 +01:00
|
|
|
h = hpoints[0].val;
|
2020-03-27 08:45:56 +01:00
|
|
|
for (let i = hpoints.length-1; i >= 0; --i) {
|
2020-01-03 21:16:40 +01:00
|
|
|
if (altitude > hpoints[i].alt) {
|
|
|
|
|
if (i == hpoints.length-1) {
|
|
|
|
|
h = hpoints[i].val;
|
|
|
|
|
} else {
|
|
|
|
|
h = hpoints[i].val + (hpoints[i+1].val - hpoints[i].val) * (altitude - hpoints[i].alt) / (hpoints[i+1].alt - hpoints[i].alt)
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-03-27 08:45:56 +01:00
|
|
|
let lpoints = ColorByAlt.air.l;
|
2020-01-03 21:16:40 +01:00
|
|
|
lpoints = lpoints.length ? lpoints : [{h:0, val:lpoints}];
|
|
|
|
|
l = lpoints[0].val;
|
2020-03-27 08:45:56 +01:00
|
|
|
for (let i = lpoints.length-1; i >= 0; --i) {
|
2020-01-03 21:16:40 +01:00
|
|
|
if (h > lpoints[i].h) {
|
|
|
|
|
if (i == lpoints.length-1) {
|
|
|
|
|
l = lpoints[i].val;
|
|
|
|
|
} else {
|
|
|
|
|
l = lpoints[i].val + (lpoints[i+1].val - lpoints[i].val) * (h - lpoints[i].h) / (lpoints[i+1].h - lpoints[i].h)
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (h < 0) {
|
|
|
|
|
h = (h % 360) + 360;
|
|
|
|
|
} else if (h >= 360) {
|
|
|
|
|
h = h % 360;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (s < 5) s = 5;
|
|
|
|
|
else if (s > 95) s = 95;
|
|
|
|
|
|
|
|
|
|
if (l < 5) l = 5;
|
|
|
|
|
else if (l > 95) l = 95;
|
|
|
|
|
|
|
|
|
|
return [h, s, l];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PlaneObject.prototype.updateIcon = function() {
|
|
|
|
|
|
2020-04-15 00:20:59 +02:00
|
|
|
let icaoType = this.icaoType;
|
2020-04-17 14:31:07 +02:00
|
|
|
if (this.icaoType == 'V22' && this.speed > 120)
|
|
|
|
|
icaoType = 'V22F';
|
2020-04-17 08:01:01 +02:00
|
|
|
if (icaoType == null && this.squawk == 7777)
|
|
|
|
|
icaoType = 'TWR';
|
2020-04-15 00:20:59 +02:00
|
|
|
|
2020-10-26 13:59:51 +01:00
|
|
|
let eastbound = this.rotation < 180;
|
2020-04-13 12:38:05 +02:00
|
|
|
let fillColor = this.getMarkerColor();
|
2020-03-27 08:45:56 +01:00
|
|
|
let baseMarkerKey = (this.category ? this.category : "A0") + "_"
|
2020-10-26 13:59:51 +01:00
|
|
|
+ this.typeDescription + "_" + this.wtc + "_" + icaoType + '_' + (this.altitude == "ground") + eastbound;
|
2020-01-03 21:16:40 +01:00
|
|
|
|
|
|
|
|
if (!this.baseMarker || this.baseMarkerKey != baseMarkerKey) {
|
|
|
|
|
this.baseMarkerKey = baseMarkerKey;
|
2020-10-26 13:59:51 +01:00
|
|
|
this.baseMarker = getBaseMarker(this.category, icaoType, this.typeDescription, this.wtc, this.addrtype, this.altitude, eastbound);
|
2020-01-03 21:16:40 +01:00
|
|
|
this.shape = this.baseMarker[0];
|
2020-05-02 18:30:53 +02:00
|
|
|
this.baseScale = this.baseMarker[1] * 0.96;
|
2020-01-03 21:16:40 +01:00
|
|
|
this.baseMarker = shapes[this.shape]
|
|
|
|
|
if (!this.baseMarker)
|
|
|
|
|
console.log(baseMarkerKey);
|
|
|
|
|
}
|
2020-04-13 12:38:05 +02:00
|
|
|
|
2020-05-03 15:34:35 +02:00
|
|
|
let strokeWidth = outlineWidth * ((this.selected && !SelectedAllPlanes && !onlySelected) ? 1.15 : 0.7);
|
2020-05-02 18:30:53 +02:00
|
|
|
strokeWidth /= this.baseScale;
|
2020-01-03 21:16:40 +01:00
|
|
|
|
|
|
|
|
this.scale = scaleFactor * this.baseScale;
|
2020-04-13 12:38:05 +02:00
|
|
|
let svgKey = fillColor + '!' + this.shape + '!' + strokeWidth;
|
2020-03-27 08:45:56 +01:00
|
|
|
let labelText = null;
|
2020-03-23 10:17:43 +01:00
|
|
|
if ( enableLabels && !showTrace && (!multiSelect || (multiSelect && this.selected)) &&
|
|
|
|
|
(
|
|
|
|
|
(ZoomLvl >= labelZoom && this.altitude != "ground")
|
|
|
|
|
|| (ZoomLvl >= labelZoomGround-2 && this.speed > 5)
|
|
|
|
|
|| ZoomLvl >= labelZoomGround
|
|
|
|
|
|| (this.selected && !SelectedAllPlanes)
|
|
|
|
|
)
|
|
|
|
|
) {
|
2020-01-15 02:46:03 +01:00
|
|
|
if (extendedLabels == 2) {
|
2020-02-01 18:02:17 +01:00
|
|
|
labelText = NBSP + (this.icaoType ? this.icaoType : " ? ") + NBSP + "\n" + NBSP + (this.registration ? this.registration : " ? ")+ NBSP + "\n" + NBSP + this.name + NBSP;
|
2020-01-15 02:46:03 +01:00
|
|
|
} else if (extendedLabels == 1 ) {
|
2020-08-16 16:39:58 +02:00
|
|
|
const altitude = (this.altitude == null) ? ' ? ' : this.altitude;
|
2020-03-21 17:11:52 +01:00
|
|
|
if ((!this.onGround || (this.speed && this.speed > 18) || (this.selected && !SelectedAllPlanes))) {
|
2020-08-16 16:39:58 +02:00
|
|
|
let speedString = (this.speed == null) ? ' ? ' : Number(this.speed).toFixed(0).toString().padStart(4, NBSP);
|
|
|
|
|
labelText = speedString + " "
|
2020-03-21 17:11:52 +01:00
|
|
|
+ altitude.toString().padStart(5, NBSP) + " \n " + this.name + " ";
|
2020-01-03 21:16:40 +01:00
|
|
|
} else {
|
|
|
|
|
labelText = " " + this.name + " ";
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
labelText = " " + this.name + " ";
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-03-27 08:45:56 +01:00
|
|
|
let styleKey = svgKey + '!' + labelText + '!' + this.scale;
|
2020-01-03 21:16:40 +01:00
|
|
|
|
|
|
|
|
if (this.markerStyle == null || this.markerIcon == null || (this.markerSvgKey != svgKey)) {
|
|
|
|
|
//console.log(this.icao + " new icon and style " + this.markerSvgKey + " -> " + svgKey);
|
|
|
|
|
|
|
|
|
|
this.markerSvgKey = svgKey;
|
|
|
|
|
this.rotationCache = this.rotation;
|
|
|
|
|
|
2020-05-21 21:25:26 +02:00
|
|
|
if (useDots) {
|
|
|
|
|
let alt = this.alt_rounded;
|
|
|
|
|
let dotKey = String(alt) + String(globalScale);
|
|
|
|
|
let circle = dotCache[dotKey];
|
|
|
|
|
if (!circle) {
|
|
|
|
|
let hsl = altitudeColor(this.altitude);
|
|
|
|
|
hsl[1] = hsl[1] * 0.85;
|
|
|
|
|
hsl[2] = hsl[2] * 0.8;
|
|
|
|
|
let col = hslToRgb(hsl);
|
|
|
|
|
circle = new ol.style.Circle({
|
|
|
|
|
radius: 3 * globalScale,
|
|
|
|
|
fill: new ol.style.Fill({
|
|
|
|
|
color: col,
|
|
|
|
|
}),
|
|
|
|
|
});
|
|
|
|
|
dotCache[dotKey] = circle;
|
|
|
|
|
}
|
|
|
|
|
this.markerIcon = circle;
|
|
|
|
|
} else if (iconCache[svgKey] == undefined) {
|
2020-04-13 12:38:05 +02:00
|
|
|
let svgURI = svgShapeToURI(this.baseMarker, fillColor, OutlineADSBColor, strokeWidth);
|
2020-03-16 08:15:17 +01:00
|
|
|
|
|
|
|
|
addToIconCache.push([svgKey, null, svgURI]);
|
|
|
|
|
|
2020-01-03 21:16:40 +01:00
|
|
|
this.markerIcon = new ol.style.Icon({
|
|
|
|
|
scale: this.scale,
|
2020-04-13 12:38:05 +02:00
|
|
|
imgSize: [this.baseMarker.w, this.baseMarker.h],
|
2020-01-03 21:16:40 +01:00
|
|
|
src: svgURI,
|
|
|
|
|
rotation: (this.baseMarker.noRotate ? 0 : this.rotation * Math.PI / 180.0),
|
|
|
|
|
rotateWithView: (this.baseMarker.noRotate ? false : true),
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
this.markerIcon = new ol.style.Icon({
|
|
|
|
|
scale: this.scale,
|
2020-04-13 12:38:05 +02:00
|
|
|
imgSize: [this.baseMarker.w, this.baseMarker.h],
|
2020-01-03 21:16:40 +01:00
|
|
|
img: iconCache[svgKey],
|
|
|
|
|
rotation: (this.baseMarker.noRotate ? 0 : this.rotation * Math.PI / 180.0),
|
|
|
|
|
rotateWithView: (this.baseMarker.noRotate ? false : true),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
//iconCache[svgKey] = undefined; // disable caching for testing
|
|
|
|
|
}
|
|
|
|
|
if (this.styleKey != styleKey) {
|
|
|
|
|
this.styleKey = styleKey;
|
|
|
|
|
if (labelText) {
|
|
|
|
|
this.markerStyle = new ol.style.Style({
|
|
|
|
|
image: this.markerIcon,
|
|
|
|
|
text: new ol.style.Text({
|
2020-04-05 20:43:50 +02:00
|
|
|
text: labelText,
|
|
|
|
|
fill: labelFill,
|
|
|
|
|
backgroundFill: bgFill,
|
|
|
|
|
stroke: labelStrokeNarrow,
|
2020-01-03 21:16:40 +01:00
|
|
|
textAlign: 'left',
|
|
|
|
|
textBaseline: "top",
|
|
|
|
|
font: labelFont,
|
2020-04-13 12:38:05 +02:00
|
|
|
offsetX: (this.baseMarker.w *0.5*0.74*this.scale),
|
|
|
|
|
offsetY: (this.baseMarker.w *0.5*0.74*this.scale),
|
2020-01-03 21:16:40 +01:00
|
|
|
}),
|
2020-07-24 18:17:53 +02:00
|
|
|
zIndex: this.zIndex,
|
2020-01-03 21:16:40 +01:00
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
this.markerStyle = new ol.style.Style({
|
|
|
|
|
image: this.markerIcon,
|
|
|
|
|
zIndex: this.zIndex,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
this.marker.setStyle(this.markerStyle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
if (this.opacityCache != opacity) {
|
|
|
|
|
this.opacityCache = opacity;
|
|
|
|
|
this.markerIcon.setOpacity(opacity);
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
|
2020-10-23 11:01:20 +02:00
|
|
|
const iconRotation = this.baseMarker.noRotate ? 0 : this.rotation;
|
|
|
|
|
if (this.rotationCache != iconRotation && Math.abs(this.rotationCache - iconRotation) > 0.35) {
|
|
|
|
|
this.rotationCache = iconRotation;
|
|
|
|
|
this.markerIcon.setRotation(iconRotation * Math.PI / 180.0);
|
2020-01-03 21:16:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.scaleCache != this.scale) {
|
|
|
|
|
this.scaleCache = this.scale;
|
|
|
|
|
this.markerIcon.setScale(this.scale);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
2020-04-19 18:07:36 +02:00
|
|
|
PlaneObject.prototype.processTrace = function() {
|
|
|
|
|
|
|
|
|
|
let options = traceOpts;
|
2020-04-18 21:10:16 +02:00
|
|
|
|
|
|
|
|
let showTime = false;
|
2020-04-18 21:27:29 +02:00
|
|
|
if (options.showTime != null) {
|
2020-04-18 21:10:16 +02:00
|
|
|
showTime = true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-19 22:19:31 +02:00
|
|
|
if (showTrace)
|
|
|
|
|
this.setNull();
|
|
|
|
|
|
2020-03-27 08:45:56 +01:00
|
|
|
let legStart = options.legStart;
|
|
|
|
|
let legEnd = options.legEnd;
|
|
|
|
|
let follow = options.follow;
|
2020-03-18 10:28:23 +01:00
|
|
|
this.checkLayers();
|
2020-03-27 08:45:56 +01:00
|
|
|
let trace = null;
|
|
|
|
|
let timeZero, _now, _last = 0;
|
2020-01-03 21:16:40 +01:00
|
|
|
this.history_size = 0;
|
2020-03-27 08:45:56 +01:00
|
|
|
let points_in_trace = 0;
|
2020-01-03 21:16:40 +01:00
|
|
|
|
2020-03-27 08:45:56 +01:00
|
|
|
let tempPlane = {};
|
2020-01-03 21:16:40 +01:00
|
|
|
const oldSegs = this.track_linesegs;
|
2020-04-18 21:10:16 +02:00
|
|
|
if (!showTime) {
|
|
|
|
|
this.track_linesegs = [];
|
|
|
|
|
this.remakeTrail();
|
|
|
|
|
}
|
2020-01-03 21:16:40 +01:00
|
|
|
|
2020-03-31 10:36:58 +02:00
|
|
|
let firstPos = null;
|
|
|
|
|
|
2020-01-03 21:16:40 +01:00
|
|
|
Object.assign(tempPlane, this);
|
|
|
|
|
|
2020-03-18 10:28:23 +01:00
|
|
|
this.position = null;
|
|
|
|
|
|
2020-03-27 08:45:56 +01:00
|
|
|
let onlyRecent = 0;
|
2020-02-06 15:13:26 +01:00
|
|
|
|
2020-10-21 17:16:39 +02:00
|
|
|
this.checkForDB(this.recentTrace);
|
|
|
|
|
this.checkForDB(this.fullTrace);
|
2020-10-20 21:31:42 +02:00
|
|
|
|
2020-02-06 18:30:14 +01:00
|
|
|
if (lastLeg && !showTrace && this.recentTrace && this.recentTrace.trace) {
|
2020-02-06 15:13:26 +01:00
|
|
|
trace = this.recentTrace.trace;
|
2020-03-27 08:45:56 +01:00
|
|
|
for (let i = trace.length - 1; i >= 0; i--) {
|
2020-02-06 15:13:26 +01:00
|
|
|
if (trace[i][6] & 2) {
|
|
|
|
|
onlyRecent = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-19 14:50:03 +02:00
|
|
|
if (this.fullTrace && this.fullTrace.trace
|
|
|
|
|
&& this.recentTrace && this.recentTrace.trace) {
|
|
|
|
|
let t1 = this.fullTrace.trace;
|
|
|
|
|
let t2 = this.recentTrace.trace;
|
|
|
|
|
let end1 = this.fullTrace.timestamp + t1[t1.length-1][0];
|
|
|
|
|
let start2 = this.recentTrace.timestamp;
|
|
|
|
|
if (end1 < start2)
|
|
|
|
|
console.log("Insufficient recent trace overlap!");
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-06 15:13:26 +01:00
|
|
|
|
2020-04-21 05:47:20 +02:00
|
|
|
let stop = 0;
|
|
|
|
|
for (let j = 0; j < 2 && !stop; j++) {
|
2020-04-19 18:07:36 +02:00
|
|
|
let start;
|
|
|
|
|
let end;
|
2020-01-10 22:34:23 +01:00
|
|
|
if (j == 0) {
|
2020-01-19 14:02:57 +01:00
|
|
|
if (!this.fullTrace || !this.fullTrace.trace)
|
|
|
|
|
continue;
|
2020-02-06 15:13:26 +01:00
|
|
|
if (onlyRecent)
|
|
|
|
|
continue;
|
2020-01-10 22:34:23 +01:00
|
|
|
timeZero = this.fullTrace.timestamp;
|
2020-01-21 10:08:20 +01:00
|
|
|
|
2020-01-10 22:34:23 +01:00
|
|
|
_last = timeZero - 1;
|
2020-01-21 10:08:20 +01:00
|
|
|
|
|
|
|
|
trace = this.fullTrace.trace;
|
2020-04-19 18:07:36 +02:00
|
|
|
|
|
|
|
|
start = 0;
|
|
|
|
|
end = trace.length;
|
|
|
|
|
if (legStart != null)
|
|
|
|
|
start = legStart;
|
|
|
|
|
if (legEnd != null)
|
|
|
|
|
end = legEnd;
|
2020-01-10 22:34:23 +01:00
|
|
|
} else {
|
2020-04-19 18:07:36 +02:00
|
|
|
if (legEnd != null)
|
|
|
|
|
continue;
|
2020-01-19 14:02:57 +01:00
|
|
|
if (!this.recentTrace || !this.recentTrace.trace)
|
|
|
|
|
continue;
|
2020-01-21 10:08:20 +01:00
|
|
|
timeZero = this.recentTrace.timestamp;
|
2020-01-19 14:02:57 +01:00
|
|
|
if (!trace) {
|
|
|
|
|
_last = timeZero - 1;
|
|
|
|
|
}
|
|
|
|
|
trace = this.recentTrace.trace;
|
2020-04-19 18:07:36 +02:00
|
|
|
start = 0;
|
|
|
|
|
end = trace.length;
|
2020-01-10 22:34:23 +01:00
|
|
|
}
|
2020-01-03 21:16:40 +01:00
|
|
|
|
2020-02-06 18:30:14 +01:00
|
|
|
if (lastLeg && !showTrace) {
|
2020-03-27 08:45:56 +01:00
|
|
|
for (let i = trace.length - 1; i >= 0; i--) {
|
2020-02-06 15:13:26 +01:00
|
|
|
if (trace[i][6] & 2) {
|
|
|
|
|
start = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-27 08:45:56 +01:00
|
|
|
for (let i = start; i < end; i++) {
|
2020-01-10 22:34:23 +01:00
|
|
|
const state = trace[i];
|
|
|
|
|
const timestamp = timeZero + state[0];
|
2020-04-18 21:10:16 +02:00
|
|
|
let stale = state[6] & 1;
|
2020-02-04 21:14:54 +01:00
|
|
|
const leg_marker = state[6] & 2;
|
2020-01-10 22:34:23 +01:00
|
|
|
|
|
|
|
|
_now = timestamp;
|
|
|
|
|
if (_now <= _last)
|
|
|
|
|
continue;
|
|
|
|
|
|
2020-04-19 18:07:36 +02:00
|
|
|
if (i == start) {
|
|
|
|
|
//console.log(timestamp);
|
|
|
|
|
//console.log(options.startStamp);
|
|
|
|
|
}
|
2020-04-21 05:47:20 +02:00
|
|
|
if (showTime && timestamp > options.showTime) {
|
2020-04-21 15:54:57 +02:00
|
|
|
if (traceOpts.showTime) {
|
2020-04-21 05:47:20 +02:00
|
|
|
if (traceOpts.replaySpeed > 0) {
|
2020-04-21 15:54:57 +02:00
|
|
|
clearTimeout(traceOpts.showTimeout);
|
|
|
|
|
let delay = (timestamp - options.showTime) / traceOpts.replaySpeed * 1000;
|
|
|
|
|
let steps = Math.round(delay / 1000);
|
|
|
|
|
traceOpts.animateInterval = delay / steps;
|
|
|
|
|
traceOpts.animateSteps = steps;
|
|
|
|
|
if (steps < 2) {
|
|
|
|
|
traceOpts.showTimeout = setTimeout(gotoTime, delay);
|
|
|
|
|
traceOpts.animate = false;
|
|
|
|
|
} else {
|
|
|
|
|
//console.timeEnd('step');
|
|
|
|
|
//console.time('step');
|
|
|
|
|
//console.log(delay);
|
|
|
|
|
traceOpts.animate = true;
|
|
|
|
|
|
|
|
|
|
let fromProj = ol.proj.fromLonLat(this.position);
|
|
|
|
|
let toProj = ol.proj.fromLonLat([state[2], state[1]]);
|
|
|
|
|
traceOpts.animateFromLon = fromProj[0]
|
|
|
|
|
traceOpts.animateFromLat = fromProj[1];
|
|
|
|
|
traceOpts.animateToLon = toProj[0];
|
|
|
|
|
traceOpts.animateToLat = toProj[1];
|
|
|
|
|
|
|
|
|
|
//console.log('from: ', fromProj);
|
|
|
|
|
//console.log('to: ', toProj);
|
|
|
|
|
|
|
|
|
|
traceOpts.showTimeout = setTimeout(gotoTime, traceOpts.animateInterval);
|
|
|
|
|
}
|
2020-04-21 05:47:20 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
traceOpts.showTime = timestamp;
|
|
|
|
|
stop = 1;
|
2020-04-18 21:10:16 +02:00
|
|
|
break;
|
2020-04-21 05:47:20 +02:00
|
|
|
}
|
2020-04-19 18:07:36 +02:00
|
|
|
if (options.startStamp != null && timestamp < options.startStamp)
|
|
|
|
|
continue;
|
|
|
|
|
if (options.endStamp != null && timestamp > options.endStamp)
|
|
|
|
|
break;
|
2020-04-18 21:10:16 +02:00
|
|
|
|
2020-01-11 10:18:56 +01:00
|
|
|
points_in_trace++;
|
|
|
|
|
|
2020-04-18 21:10:16 +02:00
|
|
|
this.updateTraceData(state, _now);
|
2020-01-10 22:34:23 +01:00
|
|
|
|
2020-03-31 10:36:58 +02:00
|
|
|
if (firstPos == null)
|
|
|
|
|
firstPos = this.position;
|
|
|
|
|
|
2020-03-22 13:50:29 +01:00
|
|
|
if (leg_marker)
|
|
|
|
|
this.leg_ts = _now;
|
2020-03-22 23:14:14 +01:00
|
|
|
if (legStart != null && legStart > 0 && legStart == i)
|
|
|
|
|
this.leg_ts = _now;
|
|
|
|
|
if (legEnd != null && legEnd < trace.length && legEnd == i + 1)
|
|
|
|
|
this.leg_ts = _now;
|
2020-03-22 13:50:29 +01:00
|
|
|
|
2020-03-22 20:54:48 +01:00
|
|
|
if (_last - _now > 320) {
|
|
|
|
|
stale = true;
|
2020-02-23 10:44:41 +01:00
|
|
|
}
|
2020-01-10 22:34:23 +01:00
|
|
|
|
2020-04-18 21:10:16 +02:00
|
|
|
if (!showTime) {
|
|
|
|
|
this.updateTrack(_now, _last, true, stale);
|
|
|
|
|
}
|
2020-01-10 22:34:23 +01:00
|
|
|
_last = _now;
|
|
|
|
|
}
|
2020-01-03 21:16:40 +01:00
|
|
|
}
|
|
|
|
|
|
2020-03-27 08:45:56 +01:00
|
|
|
for (let i = 0; i < this.trace.length; i++) {
|
2020-03-22 22:40:17 +01:00
|
|
|
if (showTrace)
|
|
|
|
|
break;
|
2020-01-03 21:16:40 +01:00
|
|
|
const state = this.trace[i];
|
2020-01-07 05:50:44 +01:00
|
|
|
if (_now >= state.now)
|
2020-01-03 21:16:40 +01:00
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
_now = state.now;
|
|
|
|
|
this.position = state.position;
|
|
|
|
|
this.position_time = _now;
|
|
|
|
|
this.altitude = state.altitude;
|
|
|
|
|
this.alt_rounded = state.alt_rounded;
|
|
|
|
|
this.speed = state.speed;
|
|
|
|
|
this.track = state.track;
|
|
|
|
|
this.rotation = state.rotation;
|
|
|
|
|
|
2020-02-23 10:44:41 +01:00
|
|
|
if (_last - _now > 30) {
|
|
|
|
|
_last = _now - 1;
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-03 21:16:40 +01:00
|
|
|
this.updateTrack(_now, _last);
|
|
|
|
|
_last = _now;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!tempPlane.prev_position) {
|
|
|
|
|
tempPlane.prev_position = this.position;
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-01 08:46:16 +02:00
|
|
|
if (tempPlane.position_time > this.position_time && !showTrace) {
|
|
|
|
|
console.log('reusing current aircraft data after processing trace.');
|
2020-03-27 08:45:56 +01:00
|
|
|
let newSegs = this.track_linesegs;
|
2020-03-31 00:19:56 +02:00
|
|
|
let newSize = this.history_size;
|
2020-01-03 21:16:40 +01:00
|
|
|
Object.assign(this, tempPlane);
|
|
|
|
|
this.track_linesegs = newSegs;
|
2020-03-31 00:19:56 +02:00
|
|
|
this.history_size = newSize;
|
2020-01-03 21:16:40 +01:00
|
|
|
}
|
2020-01-28 01:19:10 +01:00
|
|
|
|
2020-04-18 21:10:16 +02:00
|
|
|
if (showTrace && !showTime) {
|
2020-01-29 15:12:01 +01:00
|
|
|
|
2020-03-22 22:40:17 +01:00
|
|
|
if (this.track_linesegs.length > 0 && this.position) {
|
2020-01-29 15:12:01 +01:00
|
|
|
const proj = ol.proj.fromLonLat(this.position);
|
|
|
|
|
this.track_linesegs[this.track_linesegs.length - 1].fixed.appendCoordinate(proj);
|
|
|
|
|
this.track_linesegs.push({ fixed: new ol.geom.LineString([proj]),
|
|
|
|
|
feature: null,
|
|
|
|
|
estimated: false,
|
|
|
|
|
altitude: this.alt_rounded,
|
|
|
|
|
alt_real: this.altitude,
|
|
|
|
|
speed: this.speed,
|
|
|
|
|
ground: (this.altitude == "ground"),
|
|
|
|
|
ts: this.position_time,
|
|
|
|
|
track: this.rotation,
|
|
|
|
|
});
|
|
|
|
|
}
|
2020-01-28 02:18:21 +01:00
|
|
|
now = new Date().getTime()/1000;
|
2020-01-29 15:12:01 +01:00
|
|
|
}
|
2020-04-18 21:10:16 +02:00
|
|
|
if (showTrace) {
|
2020-04-19 19:30:38 +02:00
|
|
|
if (this.position_time) {
|
|
|
|
|
const date = new Date(this.position_time * 1000);
|
|
|
|
|
let timestamp =
|
|
|
|
|
date.getUTCHours().toString().padStart(2,'0')
|
|
|
|
|
+ ":" + date.getUTCMinutes().toString().padStart(2,'0')
|
|
|
|
|
+ ":" + date.getUTCSeconds().toString().padStart(2,'0')
|
|
|
|
|
+ NBSP + "Z";
|
|
|
|
|
$('#trace_time').text('UTC:\n' + timestamp);
|
|
|
|
|
} else {
|
|
|
|
|
$('#trace_time').text('UTC:\n');
|
|
|
|
|
}
|
2020-04-18 21:10:16 +02:00
|
|
|
this.seen = 0;
|
|
|
|
|
this.seen_pos = 0;
|
|
|
|
|
}
|
2020-04-29 11:07:39 +02:00
|
|
|
this.visible = true;
|
2020-04-18 21:10:16 +02:00
|
|
|
if (!showTime) {
|
|
|
|
|
this.updateFeatures(now, _last);
|
|
|
|
|
}
|
2020-01-28 01:19:10 +01:00
|
|
|
|
2020-04-21 15:54:57 +02:00
|
|
|
if (showTime && FollowSelected) {
|
|
|
|
|
OLMap.getView().setCenter(ol.proj.fromLonLat(this.position));
|
|
|
|
|
} else if (this.position && follow) {
|
|
|
|
|
toggleFollow(true);
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-27 08:45:56 +01:00
|
|
|
let mapSize = OLMap.getSize();
|
|
|
|
|
let size = [Math.max(5, mapSize[0] - 280), mapSize[1]];
|
2020-04-21 15:54:57 +02:00
|
|
|
if (!showTime && (showTrace || showTraceExit)
|
2020-04-19 18:07:36 +02:00
|
|
|
&& this.position
|
2020-04-08 19:17:25 +02:00
|
|
|
&& !noPan
|
2020-05-21 14:21:17 +02:00
|
|
|
&& !inView(this.position, myExtent(OLMap.getView().calculateExtent(size)))
|
|
|
|
|
&& !inView(firstPos, myExtent(OLMap.getView().calculateExtent(size))))
|
2020-05-01 08:46:16 +02:00
|
|
|
{
|
2020-03-22 22:52:35 +01:00
|
|
|
OLMap.getView().setCenter(ol.proj.fromLonLat(this.position));
|
2020-05-01 08:46:16 +02:00
|
|
|
}
|
|
|
|
|
|
2020-04-08 19:17:25 +02:00
|
|
|
noPan = false;
|
2020-01-28 01:19:10 +01:00
|
|
|
|
2020-01-28 02:18:21 +01:00
|
|
|
showTraceExit = false;
|
|
|
|
|
|
2020-01-19 14:02:57 +01:00
|
|
|
this.updateMarker(true);
|
2020-04-18 21:10:16 +02:00
|
|
|
if (!showTime) {
|
|
|
|
|
this.updateLines();
|
|
|
|
|
}
|
2020-03-24 19:45:03 +01:00
|
|
|
|
2020-01-22 22:21:42 +01:00
|
|
|
refreshSelected();
|
2020-04-26 19:28:44 +02:00
|
|
|
refreshTableInfo();
|
2020-01-03 21:16:40 +01:00
|
|
|
|
2020-01-11 10:18:56 +01:00
|
|
|
console.log(this.history_size + ' ' + points_in_trace);
|
2020-01-03 21:16:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update our data
|
|
|
|
|
PlaneObject.prototype.updateData = function(now, last, data, init) {
|
|
|
|
|
// get location data first, return early if only those are needed.
|
|
|
|
|
|
|
|
|
|
this.updated = true;
|
2020-03-27 08:45:56 +01:00
|
|
|
let newPos = false;
|
2020-01-03 21:16:40 +01:00
|
|
|
|
2020-03-27 08:45:56 +01:00
|
|
|
let isArray = Array.isArray(data);
|
2020-01-03 21:16:40 +01:00
|
|
|
// [.hex, .alt_baro, .gs, .track, .lat, .lon, .seen_pos, "mlat"/"tisb"/.type , .flight, .messages]
|
|
|
|
|
// 0 1 2 3 4 5 6 7 8 9
|
|
|
|
|
// this format is only valid for chunk loading the history
|
|
|
|
|
const alt_baro = isArray? data[1] : data.alt_baro;
|
|
|
|
|
const gs = isArray? data[2] : data.gs;
|
|
|
|
|
const track = isArray? data[3] : data.track;
|
|
|
|
|
const lat = isArray? data[4] : data.lat;
|
|
|
|
|
const lon = isArray? data[5] : data.lon;
|
2020-03-27 08:45:56 +01:00
|
|
|
let seen = isArray? data[6] : data.seen;
|
2020-10-01 14:23:43 +02:00
|
|
|
let seen_pos = isArray? data[6] : data.seen_pos;
|
2020-01-03 21:16:40 +01:00
|
|
|
seen = (seen == null) ? 5 : seen;
|
2020-10-01 14:23:43 +02:00
|
|
|
seen_pos = (seen_pos == null) ? 5 : seen;
|
2020-09-29 12:33:30 +02:00
|
|
|
let type = isArray? data[7] : data.type;
|
|
|
|
|
if (!isArray && data.mlat != null && data.mlat.indexOf("lat") >= 0) type = 'mlat';
|
|
|
|
|
const mlat = (type == 'mlat');
|
|
|
|
|
const tisb = (type && type.substring(0,4) == "tisb");
|
2020-01-03 21:16:40 +01:00
|
|
|
const flight = isArray? data[8] : data.flight;
|
|
|
|
|
|
|
|
|
|
this.last_message_time = now - seen;
|
|
|
|
|
|
|
|
|
|
// remember last known position even if stale
|
2020-04-14 22:39:32 +02:00
|
|
|
// some logic for parsing 978 and 1090 aircraft.jsons at the same time.
|
|
|
|
|
// more logic to not show or process mlat positions when filtered out.
|
|
|
|
|
if (lat != null && seen_pos < (now - this.position_time + 2) && !(noMLAT && mlat)) {
|
2020-01-03 21:16:40 +01:00
|
|
|
this.position = [lon, lat];
|
|
|
|
|
this.position_time = now - seen_pos;
|
|
|
|
|
newPos = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// remember last known altitude even if stale
|
2020-03-27 08:45:56 +01:00
|
|
|
let newAlt = null;
|
2020-01-03 21:16:40 +01:00
|
|
|
if (alt_baro != null) {
|
|
|
|
|
newAlt = alt_baro;
|
|
|
|
|
this.alt_baro = alt_baro;
|
|
|
|
|
} else if (data.altitude != null) {
|
|
|
|
|
newAlt = data.altitude;
|
|
|
|
|
this.alt_baro = data.altitude;
|
|
|
|
|
} else {
|
|
|
|
|
this.alt_baro = null;
|
|
|
|
|
if (data.alt_geom != null) {
|
|
|
|
|
newAlt = data.alt_geom;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Filter anything greater than 12000 fpm
|
|
|
|
|
|
|
|
|
|
|
2020-01-13 21:20:32 +01:00
|
|
|
if (newAlt == null || (newAlt == this.bad_alt && this.seen_pos > 5)) {
|
2020-01-03 21:16:40 +01:00
|
|
|
// do nothing
|
|
|
|
|
} else if (
|
|
|
|
|
!altitudeFilter
|
|
|
|
|
|| this.altitude == null
|
|
|
|
|
|| newAlt == "ground"
|
|
|
|
|
|| this.altitude == "ground"
|
|
|
|
|
|| (seen_pos != null && seen_pos < 2)
|
|
|
|
|
) {
|
|
|
|
|
this.altitude = newAlt;
|
|
|
|
|
this.altitudeTime = now;
|
|
|
|
|
} else if (
|
|
|
|
|
this.alt_reliable > 0 && this.altBad(newAlt, this.altitude, this.altitudeTime, data)
|
|
|
|
|
&& (this.bad_alt == null || this.altBad(newAlt, this.bad_alt, this.bad_altTime, data))
|
|
|
|
|
) {
|
|
|
|
|
// filter this altitude!
|
|
|
|
|
this.alt_reliable--;
|
|
|
|
|
this.bad_alt = newAlt;
|
|
|
|
|
this.bad_altTime = now;
|
|
|
|
|
if (debugPosFilter) {
|
|
|
|
|
console.log((now%1000).toFixed(0) + ': AltFilter: ' + this.icao
|
|
|
|
|
+ ' oldAlt: ' + this.altitude
|
|
|
|
|
+ ' newAlt: ' + newAlt
|
|
|
|
|
+ ' elapsed: ' + (now-this.altitudeTime).toFixed(0) );
|
|
|
|
|
jumpTo = this.icao;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// good altitude
|
|
|
|
|
this.altitude = newAlt;
|
|
|
|
|
this.altitudeTime = now;
|
|
|
|
|
this.alt_reliable = Math.min(this.alt_reliable + 1, 3);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.alt_rounded = calcAltitudeRounded(this.altitude);
|
|
|
|
|
|
|
|
|
|
if (this.altitude == null) {
|
|
|
|
|
this.onGround = null;
|
2020-04-13 09:47:56 +02:00
|
|
|
this.zIndex = 10;
|
2020-01-03 21:16:40 +01:00
|
|
|
} else if (this.altitude == "ground") {
|
|
|
|
|
this.onGround = true;
|
2020-04-13 09:47:56 +02:00
|
|
|
this.zIndex = 5;
|
2020-01-03 21:16:40 +01:00
|
|
|
} else {
|
|
|
|
|
this.onGround = false;
|
2020-07-24 18:17:53 +02:00
|
|
|
this.zIndex = this.altitude + 10000;
|
2020-04-13 09:47:56 +02:00
|
|
|
}
|
|
|
|
|
if (this.category == 'C3' || this.icaoType == 'TWR') {
|
|
|
|
|
this.zIndex = 1;
|
2020-01-03 21:16:40 +01:00
|
|
|
}
|
2020-07-09 16:20:43 +02:00
|
|
|
if (this.icao[0] == '~')
|
|
|
|
|
this.zIndex -= 100000;
|
2020-01-03 21:16:40 +01:00
|
|
|
|
|
|
|
|
// needed for track labels
|
|
|
|
|
this.speed = gs;
|
|
|
|
|
|
2020-01-12 15:13:35 +01:00
|
|
|
this.track = track;
|
2020-01-03 21:16:40 +01:00
|
|
|
if (track != null) {
|
|
|
|
|
this.rotation = track;
|
|
|
|
|
this.request_rotation_from_track = false;
|
2020-01-07 05:50:44 +01:00
|
|
|
} else if (data.calc_track) {
|
|
|
|
|
this.rotation = data.calc_track;
|
2020-01-03 21:16:40 +01:00
|
|
|
} else {
|
|
|
|
|
this.request_rotation_from_track = true;
|
|
|
|
|
}
|
|
|
|
|
// don't expire callsigns
|
|
|
|
|
if (flight != null) {
|
|
|
|
|
this.flight = flight;
|
|
|
|
|
this.name = flight;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mlat && noMLAT) {
|
2020-04-21 03:06:33 +02:00
|
|
|
this.dataSource = "mode_s";
|
2020-01-03 21:16:40 +01:00
|
|
|
} else if (mlat) {
|
|
|
|
|
this.dataSource = "mlat";
|
|
|
|
|
} else if (!displayUATasADSB && this.receiver == "uat" && !tisb) {
|
|
|
|
|
this.dataSource = "uat";
|
|
|
|
|
} else if (tisb) {
|
|
|
|
|
this.dataSource = "tisb";
|
2020-04-27 12:22:14 +02:00
|
|
|
} else if ((lat != null && type == null) || (type && (type.substring(0,4) == "adsb" || type.substring(0,4) == "adsr"))) {
|
2020-01-03 21:16:40 +01:00
|
|
|
this.dataSource = "adsb";
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-18 21:10:16 +02:00
|
|
|
if (data.type == 'adsc') {
|
2020-04-21 03:39:28 +02:00
|
|
|
this.dataSource = "adsc";
|
2020-04-21 03:06:33 +02:00
|
|
|
} else if (data.type == 'unknown' || data.type == 'other') {
|
2020-04-18 21:10:16 +02:00
|
|
|
this.dataSource = "unknown";
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-03 21:16:40 +01:00
|
|
|
if (isArray) {
|
|
|
|
|
this.messages = data[9];
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update all of our data
|
|
|
|
|
|
2020-01-14 10:27:36 +01:00
|
|
|
if (now - this.last > 0) {
|
|
|
|
|
if (this.receiver == "1090") {
|
|
|
|
|
const messageRate = (data.messages - this.msgs1090)/(now - this.last);
|
|
|
|
|
this.messageRate = (messageRate + this.messageRateOld)/2;
|
|
|
|
|
this.messageRateOld = messageRate;
|
|
|
|
|
this.msgs1090 = data.messages;
|
|
|
|
|
} else {
|
|
|
|
|
const messageRate = (data.messages - this.msgs978)/(uat_now - uat_last);
|
|
|
|
|
this.messageRate = (messageRate + this.messageRateOld)/2;
|
|
|
|
|
this.messageRateOld = messageRate;
|
|
|
|
|
this.msgs978 = data.messages;
|
|
|
|
|
}
|
2020-01-03 21:16:40 +01:00
|
|
|
}
|
|
|
|
|
this.messages = data.messages;
|
|
|
|
|
|
|
|
|
|
this.rssi = data.rssi;
|
|
|
|
|
|
|
|
|
|
if (data.gs != null)
|
|
|
|
|
this.gs = data.gs;
|
|
|
|
|
else if (data.speed != null)
|
|
|
|
|
this.gs = data.speed;
|
|
|
|
|
else
|
|
|
|
|
this.gs = null;
|
|
|
|
|
|
|
|
|
|
if (data.baro_rate != null)
|
|
|
|
|
this.baro_rate = data.baro_rate;
|
|
|
|
|
else if (data.vert_rate != null)
|
|
|
|
|
this.baro_rate = data.vert_rate;
|
|
|
|
|
else
|
|
|
|
|
this.baro_rate = null;
|
|
|
|
|
|
|
|
|
|
// simple fields
|
|
|
|
|
this.alt_geom = data.alt_geom;
|
|
|
|
|
this.ias = data.ias;
|
|
|
|
|
this.tas = data.tas;
|
|
|
|
|
this.track_rate = data.track_rate;
|
|
|
|
|
this.mag_heading = data.mag_heading;
|
|
|
|
|
this.mach = data.mach;
|
|
|
|
|
this.roll = data.roll;
|
|
|
|
|
this.nav_altitude = data.nav_altitude;
|
|
|
|
|
this.nav_heading = data.nav_heading;
|
|
|
|
|
this.nav_modes = data.nav_modes;
|
|
|
|
|
this.nac_p = data.nac_p;
|
|
|
|
|
this.nac_v = data.nac_v;
|
|
|
|
|
this.nic_baro = data.nic_baro;
|
|
|
|
|
this.sil_type = data.sil_type;
|
|
|
|
|
this.sil = data.sil;
|
|
|
|
|
this.nav_qnh = data.nav_qnh;
|
|
|
|
|
this.geom_rate = data.geom_rate;
|
|
|
|
|
this.rc = data.rc;
|
|
|
|
|
this.squawk = data.squawk;
|
2020-03-31 09:41:04 +02:00
|
|
|
this.wd = data.wd;
|
|
|
|
|
this.ws = data.ws;
|
|
|
|
|
this.oat = data.oat;
|
2020-04-22 08:53:27 +02:00
|
|
|
this.tat = data.tat;
|
2020-01-03 21:16:40 +01:00
|
|
|
|
|
|
|
|
// fields with more complex behaviour
|
|
|
|
|
|
|
|
|
|
if (data.version != null) {
|
|
|
|
|
this.version = data.version;
|
|
|
|
|
}
|
|
|
|
|
if (data.category != null) {
|
|
|
|
|
this.category = data.category;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (data.true_heading != null)
|
|
|
|
|
this.true_heading = data.true_heading;
|
|
|
|
|
else
|
|
|
|
|
this.true_heading = null;
|
|
|
|
|
|
|
|
|
|
if (data.type != null)
|
2020-08-26 20:50:20 +02:00
|
|
|
this.addrtype = data.type;
|
2020-01-03 21:16:40 +01:00
|
|
|
else
|
|
|
|
|
this.addrtype = null;
|
|
|
|
|
|
|
|
|
|
// Pick a selected altitude
|
|
|
|
|
if (data.nav_altitude_fms != null) {
|
|
|
|
|
this.nav_altitude = data.nav_altitude_fms;
|
|
|
|
|
} else if (data.nav_altitude_mcp != null){
|
|
|
|
|
this.nav_altitude = data.nav_altitude_mcp;
|
|
|
|
|
} else {
|
|
|
|
|
this.nav_altitude = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Pick vertical rate from either baro or geom rate
|
|
|
|
|
// geometric rate is generally more reliable (smoothed etc)
|
|
|
|
|
if (data.geom_rate != null ) {
|
|
|
|
|
this.vert_rate = data.geom_rate;
|
|
|
|
|
} else if (data.baro_rate != null) {
|
|
|
|
|
this.vert_rate = data.baro_rate;
|
|
|
|
|
} else if (data.vert_rate != null) {
|
|
|
|
|
// legacy from mut v 1.15
|
|
|
|
|
this.vert_rate = data.vert_rate;
|
|
|
|
|
} else {
|
|
|
|
|
this.vert_rate = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.request_rotation_from_track = false;
|
|
|
|
|
if (this.altitude == "ground") {
|
|
|
|
|
if (this.true_heading != null)
|
|
|
|
|
this.rotation = this.true_heading;
|
|
|
|
|
else if (this.mag_heading != null)
|
|
|
|
|
this.rotation = this.mag_heading;
|
2020-01-07 05:50:44 +01:00
|
|
|
else if (data.calc_track)
|
|
|
|
|
this.rotation = data.calc_track;
|
2020-01-03 21:16:40 +01:00
|
|
|
} else if (this.track != null) {
|
|
|
|
|
this.rotation = this.track;
|
|
|
|
|
} else if (this.true_heading != null) {
|
|
|
|
|
this.rotation = this.true_heading;
|
|
|
|
|
} else if (this.mag_heading != null) {
|
|
|
|
|
this.rotation = this.mag_heading;
|
2020-01-07 05:50:44 +01:00
|
|
|
} else if (data.calc_track) {
|
|
|
|
|
this.rotation = data.calc_track;
|
2020-01-03 21:16:40 +01:00
|
|
|
} else {
|
|
|
|
|
this.request_rotation_from_track = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (globeIndex && newPos) {
|
2020-03-27 08:45:56 +01:00
|
|
|
let state = {};
|
2020-01-03 21:16:40 +01:00
|
|
|
state.now = this.position_time;
|
|
|
|
|
state.position = this.position;
|
|
|
|
|
state.altitude = this.altitude;
|
|
|
|
|
state.alt_rounded = this.alt_rounded;
|
|
|
|
|
state.speed = this.speed;
|
|
|
|
|
state.track = this.track;
|
|
|
|
|
state.rotation = this.rotation;
|
|
|
|
|
this.trace.push(state);
|
2020-03-16 16:25:52 +01:00
|
|
|
if (this.trace.length > 20) {
|
|
|
|
|
this.trace.slice(-15);
|
2020-01-03 21:16:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
2020-01-14 10:27:36 +01:00
|
|
|
|
2020-10-21 17:16:39 +02:00
|
|
|
this.checkForDB(data);
|
2020-10-20 21:31:42 +02:00
|
|
|
|
2020-01-14 10:27:36 +01:00
|
|
|
this.last = now;
|
2020-01-03 21:16:40 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PlaneObject.prototype.updateTick = function(redraw) {
|
|
|
|
|
if (this.dataSource == "uat")
|
|
|
|
|
this.updateFeatures(uat_now, uat_last, redraw);
|
|
|
|
|
else
|
|
|
|
|
this.updateFeatures(now, last, redraw);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PlaneObject.prototype.updateFeatures = function(now, last, redraw) {
|
|
|
|
|
|
|
|
|
|
// recompute seen and seen_pos
|
2020-01-28 02:18:21 +01:00
|
|
|
this.seen = Math.max(0, now - this.last_message_time)
|
|
|
|
|
this.seen_pos = Math.max(0, now - this.position_time);
|
2020-01-03 21:16:40 +01:00
|
|
|
|
2020-03-04 16:25:29 +01:00
|
|
|
if (globeIndex && this.isFiltered())
|
|
|
|
|
return;
|
|
|
|
|
|
2020-03-27 08:45:56 +01:00
|
|
|
let moved = false;
|
2020-01-25 17:50:42 +01:00
|
|
|
|
2020-01-03 21:16:40 +01:00
|
|
|
if (this.updated) {
|
|
|
|
|
if (this.flight && this.flight.trim()) {
|
|
|
|
|
this.name = this.flight;
|
|
|
|
|
} else if (this.registration) {
|
|
|
|
|
this.name = '_' + this.registration;
|
|
|
|
|
} else {
|
|
|
|
|
this.name = '_' + this.icao.toUpperCase();
|
|
|
|
|
}
|
|
|
|
|
this.name = this.name.trim();
|
2020-01-25 17:50:42 +01:00
|
|
|
|
|
|
|
|
moved = this.updateTrack(now, last);
|
2020-01-03 21:16:40 +01:00
|
|
|
}
|
2020-09-26 18:03:24 +02:00
|
|
|
if (!this.isFiltered() && this.checkVisible()) {
|
2020-01-25 17:50:42 +01:00
|
|
|
const lastVisible = this.visible;
|
2020-01-03 21:16:40 +01:00
|
|
|
this.visible = true;
|
2020-01-13 11:03:59 +01:00
|
|
|
if (SelectedAllPlanes)
|
2020-01-03 21:16:40 +01:00
|
|
|
this.selected = true;
|
|
|
|
|
|
2020-03-27 08:45:56 +01:00
|
|
|
let lines = false;
|
|
|
|
|
let marker = false;
|
2020-01-26 20:33:16 +01:00
|
|
|
|
2020-02-04 13:17:48 +01:00
|
|
|
|
|
|
|
|
marker = true;
|
|
|
|
|
/*
|
2020-01-26 20:33:16 +01:00
|
|
|
this.scale = scaleFactor * this.baseScale;
|
|
|
|
|
if (this.scaleCache != this.scale)
|
|
|
|
|
marker = true;
|
2020-02-04 13:17:48 +01:00
|
|
|
*/
|
2020-01-26 20:33:16 +01:00
|
|
|
if (redraw || moved || lastVisible != this.visible)
|
|
|
|
|
marker = lines = true;
|
|
|
|
|
|
|
|
|
|
if (lines)
|
2020-01-03 21:16:40 +01:00
|
|
|
this.updateLines();
|
2020-04-22 14:28:08 +02:00
|
|
|
if (marker) {
|
2020-01-03 21:16:40 +01:00
|
|
|
this.updateMarker(true);
|
2020-04-22 14:28:08 +02:00
|
|
|
if (this == SelectedPlane && FollowSelected && this.position)
|
|
|
|
|
OLMap.getView().setCenter(ol.proj.fromLonLat(this.position));
|
|
|
|
|
}
|
2020-01-03 21:16:40 +01:00
|
|
|
} else {
|
|
|
|
|
if (this.visible) {
|
|
|
|
|
//console.log("hiding " + this.icao);
|
|
|
|
|
this.clearMarker();
|
|
|
|
|
this.clearLines();
|
|
|
|
|
this.visible = false;
|
|
|
|
|
this.selected = false;
|
|
|
|
|
if (SelectedPlane == this.icao)
|
|
|
|
|
selectPlaneByHex(null,false);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-01-14 11:08:10 +01:00
|
|
|
this.updated = false;
|
2020-01-03 21:16:40 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PlaneObject.prototype.clearMarker = function() {
|
|
|
|
|
if (this.marker && this.marker.visible) {
|
2020-03-16 18:12:59 +01:00
|
|
|
PlaneIconFeatures.removeFeature(this.marker);
|
2020-01-03 21:16:40 +01:00
|
|
|
this.marker.visible = false;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Update our marker on the map
|
|
|
|
|
PlaneObject.prototype.updateMarker = function(moved) {
|
2020-08-05 23:27:28 +02:00
|
|
|
if (pTracks)
|
|
|
|
|
return;
|
2020-01-03 21:16:40 +01:00
|
|
|
if (!this.visible || this.position == null || this.isFiltered()) {
|
|
|
|
|
this.clearMarker();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!this.marker) {
|
|
|
|
|
this.marker = new ol.Feature(new ol.geom.Point(ol.proj.fromLonLat(this.position)));
|
|
|
|
|
this.marker.hex = this.icao;
|
2020-03-16 18:12:59 +01:00
|
|
|
PlaneIconFeatures.addFeature(this.marker);
|
2020-01-03 21:16:40 +01:00
|
|
|
this.marker.visible = true;
|
|
|
|
|
} else if (moved) {
|
|
|
|
|
this.marker.setGeometry(new ol.geom.Point(ol.proj.fromLonLat(this.position)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.updateIcon();
|
|
|
|
|
|
|
|
|
|
if (!this.marker.visible) {
|
|
|
|
|
this.marker.visible = true;
|
2020-03-16 18:12:59 +01:00
|
|
|
PlaneIconFeatures.addFeature(this.marker);
|
2020-01-03 21:16:40 +01:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// return the styling of the lines based on altitude
|
2020-03-03 14:51:34 +01:00
|
|
|
function altitudeLines (segment) {
|
2020-03-27 08:45:56 +01:00
|
|
|
let colorArr = altitudeColor(segment.altitude);
|
2020-01-05 20:34:25 +01:00
|
|
|
if (segment.estimated)
|
2020-04-08 01:46:21 +02:00
|
|
|
colorArr = [colorArr[0], colorArr[1], colorArr[2] * 0.8];
|
2020-03-27 08:45:56 +01:00
|
|
|
//let color = 'hsl(' + colorArr[0].toFixed(0) + ', ' + colorArr[1].toFixed(0) + '%, ' + colorArr[2].toFixed(0) + '%)';
|
2020-02-29 19:04:08 +01:00
|
|
|
|
2020-04-13 09:47:56 +02:00
|
|
|
let color = hslToRgb(colorArr);
|
2020-02-07 07:17:01 +01:00
|
|
|
|
2020-02-29 20:01:51 +01:00
|
|
|
if (monochromeTracks)
|
|
|
|
|
color = monochromeTracks;
|
2020-02-29 19:04:08 +01:00
|
|
|
|
2020-02-07 07:17:01 +01:00
|
|
|
const lineKey = color + '_' + debugTracks + '_' + noVanish + '_' + segment.estimated + '_' + newWidth;
|
2020-01-03 21:16:40 +01:00
|
|
|
|
|
|
|
|
if (lineStyleCache[lineKey])
|
|
|
|
|
return lineStyleCache[lineKey];
|
|
|
|
|
|
2020-04-06 09:23:52 +02:00
|
|
|
let multiplier = segment.estimated ? 0.6 : 1;
|
|
|
|
|
|
2020-04-15 11:02:36 +02:00
|
|
|
if (noVanish)
|
2020-04-06 09:23:52 +02:00
|
|
|
multiplier *= (segment.estimated ? 0.3 : 0.6);
|
2020-01-05 20:34:25 +01:00
|
|
|
|
2020-08-06 13:26:41 +02:00
|
|
|
let join = 'round';
|
|
|
|
|
let cap = 'square';
|
2020-01-03 21:16:40 +01:00
|
|
|
if (!debugTracks) {
|
2020-04-15 11:02:36 +02:00
|
|
|
if (segment.estimated && !noVanish) {
|
2020-04-08 01:46:21 +02:00
|
|
|
lineStyleCache[lineKey] = [
|
|
|
|
|
new ol.style.Style({
|
|
|
|
|
stroke: new ol.style.Stroke({
|
|
|
|
|
color: 'black',
|
|
|
|
|
width: 2 * newWidth * 0.3,
|
2020-08-06 13:26:41 +02:00
|
|
|
lineJoin: join,
|
|
|
|
|
lineCap: cap,
|
2020-04-08 01:46:21 +02:00
|
|
|
})
|
|
|
|
|
}),
|
|
|
|
|
new ol.style.Style({
|
|
|
|
|
stroke: new ol.style.Stroke({
|
|
|
|
|
color: color,
|
|
|
|
|
width: 2 * newWidth,
|
2020-04-20 16:29:12 +02:00
|
|
|
lineDash: [10, 20 + 3 * newWidth],
|
|
|
|
|
lineDashOffset: 5,
|
2020-08-06 13:26:41 +02:00
|
|
|
lineJoin: join,
|
|
|
|
|
lineCap: cap,
|
2020-04-08 01:46:21 +02:00
|
|
|
}),
|
2020-04-05 18:59:54 +02:00
|
|
|
})
|
2020-04-08 01:46:21 +02:00
|
|
|
];
|
2020-09-16 18:56:15 +02:00
|
|
|
} else if (segment.estimated && pTracks) {
|
|
|
|
|
lineStyleCache[lineKey] = new ol.style.Style({});
|
2020-04-05 18:59:54 +02:00
|
|
|
} else {
|
|
|
|
|
lineStyleCache[lineKey] = new ol.style.Style({
|
|
|
|
|
stroke: new ol.style.Stroke({
|
|
|
|
|
color: color,
|
2020-04-06 09:23:52 +02:00
|
|
|
width: 2 * newWidth * multiplier,
|
2020-08-06 13:26:41 +02:00
|
|
|
lineJoin: join,
|
|
|
|
|
lineCap: cap,
|
2020-04-05 18:59:54 +02:00
|
|
|
})
|
|
|
|
|
});
|
|
|
|
|
}
|
2020-01-03 21:16:40 +01:00
|
|
|
} else {
|
2020-09-16 18:56:15 +02:00
|
|
|
if (segment.noLabel || segment.estimated) {
|
|
|
|
|
lineStyleCache[lineKey] = [
|
|
|
|
|
new ol.style.Style({
|
|
|
|
|
stroke: new ol.style.Stroke({
|
|
|
|
|
color: color,
|
|
|
|
|
width: 2 * newWidth * multiplier,
|
|
|
|
|
lineJoin: join,
|
|
|
|
|
lineCap: cap,
|
2020-01-03 21:16:40 +01:00
|
|
|
})
|
|
|
|
|
}),
|
2020-09-16 18:56:15 +02:00
|
|
|
];
|
|
|
|
|
} else {
|
|
|
|
|
lineStyleCache[lineKey] = [
|
|
|
|
|
new ol.style.Style({
|
|
|
|
|
image: new ol.style.Circle({
|
|
|
|
|
radius: 2 * newWidth,
|
|
|
|
|
fill: new ol.style.Fill({
|
|
|
|
|
color: color
|
|
|
|
|
})
|
|
|
|
|
}),
|
|
|
|
|
geometry: function(feature) {
|
|
|
|
|
return new ol.geom.MultiPoint(feature.getGeometry().getCoordinates());
|
|
|
|
|
}
|
|
|
|
|
}),
|
|
|
|
|
new ol.style.Style({
|
|
|
|
|
stroke: new ol.style.Stroke({
|
|
|
|
|
color: color,
|
|
|
|
|
width: 2 * newWidth * multiplier,
|
|
|
|
|
lineJoin: join,
|
|
|
|
|
lineCap: cap,
|
|
|
|
|
})
|
2020-01-03 21:16:40 +01:00
|
|
|
})
|
2020-09-16 18:56:15 +02:00
|
|
|
];
|
|
|
|
|
}
|
2020-01-03 21:16:40 +01:00
|
|
|
}
|
|
|
|
|
return lineStyleCache[lineKey];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update our planes tail line,
|
|
|
|
|
PlaneObject.prototype.updateLines = function() {
|
2020-01-28 01:19:10 +01:00
|
|
|
if (!this.visible || this.position == null || (!this.selected && !SelectedAllPlanes) || this.isFiltered())
|
2020-01-03 21:16:40 +01:00
|
|
|
return this.clearLines();
|
|
|
|
|
|
|
|
|
|
if (this.track_linesegs.length == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
2020-03-18 10:28:23 +01:00
|
|
|
this.checkLayers();
|
|
|
|
|
|
2020-03-27 08:45:56 +01:00
|
|
|
let trail_add = [];
|
|
|
|
|
let label_add = [];
|
2020-03-18 10:28:23 +01:00
|
|
|
|
|
|
|
|
if (!this.layer.getVisible())
|
2020-01-03 21:16:40 +01:00
|
|
|
this.layer.setVisible(true);
|
2020-03-18 10:28:23 +01:00
|
|
|
|
2020-03-23 10:17:43 +01:00
|
|
|
if (trackLabels || showTrace) {
|
|
|
|
|
if (!this.layer_labels.getVisible())
|
|
|
|
|
this.layer_labels.setVisible(true);
|
|
|
|
|
} else if (this.layer_labels && this.layer_labels.getVisible()) {
|
2020-03-18 10:28:23 +01:00
|
|
|
this.layer_labels.setVisible(false);
|
2020-03-23 10:17:43 +01:00
|
|
|
}
|
2020-01-03 21:16:40 +01:00
|
|
|
|
|
|
|
|
// create the new elastic band feature
|
2020-04-21 15:54:57 +02:00
|
|
|
if (this.elastic_feature) {
|
2020-03-18 10:28:23 +01:00
|
|
|
this.trail_features.removeFeature(this.elastic_feature);
|
2020-04-21 15:54:57 +02:00
|
|
|
this.elastic_feature = null;
|
|
|
|
|
}
|
2020-03-18 10:28:23 +01:00
|
|
|
|
2020-03-27 08:45:56 +01:00
|
|
|
let lastseg = this.track_linesegs[this.track_linesegs.length - 1];
|
|
|
|
|
let lastfixed = lastseg.fixed.getCoordinateAt(1.0);
|
|
|
|
|
let geom = new ol.geom.LineString([lastfixed, ol.proj.fromLonLat(this.position)]);
|
2020-03-18 10:28:23 +01:00
|
|
|
|
|
|
|
|
|
2020-04-21 15:54:57 +02:00
|
|
|
if (!showTrace) {
|
|
|
|
|
this.elastic_feature = new ol.Feature(geom);
|
2020-05-17 10:45:31 +02:00
|
|
|
if (filterTracks && altFiltered(lastseg.altitude)) {
|
2020-04-21 15:54:57 +02:00
|
|
|
this.elastic_feature.setStyle(nullStyle);
|
|
|
|
|
} else {
|
|
|
|
|
this.elastic_feature.setStyle(altitudeLines(lastseg));
|
|
|
|
|
}
|
2020-01-03 21:16:40 +01:00
|
|
|
|
2020-04-21 15:54:57 +02:00
|
|
|
trail_add.push(this.elastic_feature);
|
|
|
|
|
}
|
2020-01-03 21:16:40 +01:00
|
|
|
|
|
|
|
|
// create any missing fixed line features
|
|
|
|
|
|
2020-03-27 08:45:56 +01:00
|
|
|
for (let i = this.track_linesegs.length-1; i >= 0; i--) {
|
|
|
|
|
let seg = this.track_linesegs[i];
|
2020-01-03 21:16:40 +01:00
|
|
|
if (seg.feature && (!trackLabels || seg.label))
|
|
|
|
|
break;
|
|
|
|
|
|
2020-05-17 10:45:31 +02:00
|
|
|
if ((filterTracks && altFiltered(seg.altitude)) || altitudeLines(seg) == nullStyle) {
|
2020-01-03 21:16:40 +01:00
|
|
|
seg.feature = true;
|
|
|
|
|
} else if (!seg.feature) {
|
|
|
|
|
seg.feature = new ol.Feature(seg.fixed);
|
2020-03-03 14:51:34 +01:00
|
|
|
seg.feature.setStyle(altitudeLines(seg));
|
2020-01-03 21:16:40 +01:00
|
|
|
seg.feature.hex = this.icao;
|
2020-04-18 21:10:16 +02:00
|
|
|
seg.feature.timestamp = seg.ts;
|
2020-03-18 10:28:23 +01:00
|
|
|
trail_add.push(seg.feature);
|
2020-01-03 21:16:40 +01:00
|
|
|
}
|
|
|
|
|
|
2020-04-03 11:04:35 +02:00
|
|
|
if (seg.label) {
|
|
|
|
|
// nothing to do, label already present
|
2020-05-17 10:45:31 +02:00
|
|
|
} else if ((filterTracks && altFiltered(seg.altitude)) || seg.noLabel) {
|
2020-01-03 21:16:40 +01:00
|
|
|
seg.label = true;
|
2020-04-03 11:04:35 +02:00
|
|
|
} else if (
|
|
|
|
|
trackLabels ||
|
|
|
|
|
((i == 0 || i == this.track_linesegs.length-1 ||seg.leg) && showTrace && enableLabels)
|
|
|
|
|
) {
|
2020-03-21 17:11:52 +01:00
|
|
|
const alt_real = (seg.alt_real != null) ? seg.alt_real : 'n/a';
|
2020-05-08 17:58:16 +02:00
|
|
|
const speed = (seg.speed != null) ? seg.speed.toFixed(0).toString() : 'n/a';
|
2020-01-03 21:16:40 +01:00
|
|
|
seg.label = new ol.Feature(new ol.geom.Point(seg.fixed.getFirstCoordinate()));
|
2020-03-27 08:45:56 +01:00
|
|
|
let timestamp;
|
2020-03-30 10:46:26 +02:00
|
|
|
const date = new Date(seg.ts * 1000);
|
2020-01-20 02:03:23 +01:00
|
|
|
if (showTrace) {
|
2020-01-22 22:21:42 +01:00
|
|
|
timestamp =
|
|
|
|
|
date.getUTCHours().toString().padStart(2,'0')
|
2020-01-20 02:03:23 +01:00
|
|
|
+ ":" + date.getUTCMinutes().toString().padStart(2,'0')
|
|
|
|
|
+ ":" + date.getUTCSeconds().toString().padStart(2,'0');
|
2020-03-19 09:00:28 +01:00
|
|
|
timestamp = "".padStart(0, NBSP) + timestamp + NBSP + "Z";
|
2020-03-30 10:46:26 +02:00
|
|
|
if (traceDay != date.getUTCDate())
|
|
|
|
|
timestamp = "".padStart(0, NBSP) + zDateString(date) + '\n' + timestamp;
|
2020-01-20 02:03:23 +01:00
|
|
|
} else {
|
|
|
|
|
timestamp = date.getHours().toString().padStart(2,'0')
|
|
|
|
|
+ ":" + date.getMinutes().toString().padStart(2,'0')
|
|
|
|
|
+ ":" + date.getSeconds().toString().padStart(2,'0');
|
2020-03-19 09:00:28 +01:00
|
|
|
timestamp = "".padStart(2, NBSP) + timestamp;
|
2020-03-30 10:46:26 +02:00
|
|
|
if (today != date.getDate())
|
|
|
|
|
timestamp = "".padStart(0, NBSP) + lDateString(date) + '\n' + timestamp;
|
2020-01-20 02:03:23 +01:00
|
|
|
}
|
2020-03-27 08:45:56 +01:00
|
|
|
let text =
|
2020-05-08 17:58:16 +02:00
|
|
|
speed.padStart(3, NBSP) + " "
|
2020-03-21 17:11:52 +01:00
|
|
|
+ (alt_real == "ground" ? ("Ground") : (alt_real.toString().padStart(6, NBSP)))
|
2020-01-03 21:16:40 +01:00
|
|
|
+ "\n"
|
2020-01-14 15:02:04 +01:00
|
|
|
//+ NBSP + format_track_arrow(seg.track)
|
2020-01-20 02:03:23 +01:00
|
|
|
+ timestamp;
|
2020-03-23 10:17:43 +01:00
|
|
|
|
|
|
|
|
if (showTrace && !trackLabels)
|
|
|
|
|
text = timestamp;
|
|
|
|
|
|
2020-03-27 08:45:56 +01:00
|
|
|
let fill = labelFill;
|
2020-04-03 11:04:35 +02:00
|
|
|
let zIndex = -i - 50 * (seg.alt_real == null);
|
2020-03-22 13:50:29 +01:00
|
|
|
if (seg.leg == 'start') {
|
|
|
|
|
fill = new ol.style.Fill({color: '#88CC88' });
|
2020-04-21 02:50:34 +02:00
|
|
|
zIndex += 123499;
|
2020-03-22 13:50:29 +01:00
|
|
|
}
|
|
|
|
|
if (seg.leg == 'end') {
|
|
|
|
|
fill = new ol.style.Fill({color: '#8888CC' });
|
2020-04-19 22:19:31 +02:00
|
|
|
zIndex += 123455;
|
2020-03-22 13:50:29 +01:00
|
|
|
}
|
2020-01-03 21:16:40 +01:00
|
|
|
seg.label.setStyle(
|
|
|
|
|
new ol.style.Style({
|
|
|
|
|
text: new ol.style.Text({
|
|
|
|
|
text: text,
|
2020-03-22 13:50:29 +01:00
|
|
|
fill: fill,
|
2020-03-18 11:18:48 +01:00
|
|
|
stroke: labelStroke,
|
2020-01-03 21:16:40 +01:00
|
|
|
textAlign: 'left',
|
|
|
|
|
textBaseline: "top",
|
|
|
|
|
font: labelFont,
|
2020-03-18 17:02:31 +01:00
|
|
|
offsetX: 8 * globalScale,
|
|
|
|
|
offsetY: 8 * globalScale,
|
2020-01-03 21:16:40 +01:00
|
|
|
}),
|
2020-03-18 11:18:48 +01:00
|
|
|
image: new ol.style.Circle({
|
|
|
|
|
radius: 2 * globalScale,
|
|
|
|
|
fill: blackFill,
|
|
|
|
|
}),
|
2020-03-22 13:50:29 +01:00
|
|
|
zIndex: zIndex,
|
2020-01-03 21:16:40 +01:00
|
|
|
})
|
|
|
|
|
);
|
|
|
|
|
seg.label.hex = this.icao;
|
2020-03-18 10:28:23 +01:00
|
|
|
label_add.push(seg.label)
|
2020-01-03 21:16:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-18 10:28:23 +01:00
|
|
|
if (trail_add.length > 0)
|
|
|
|
|
this.trail_features.addFeatures(trail_add);
|
2020-03-23 10:17:43 +01:00
|
|
|
if (this.trail_labels && label_add.length > 0)
|
2020-03-18 10:28:23 +01:00
|
|
|
this.trail_labels.addFeatures(label_add);
|
2020-01-03 21:16:40 +01:00
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
PlaneObject.prototype.remakeTrail = function() {
|
|
|
|
|
|
2020-03-18 10:28:23 +01:00
|
|
|
if (this.trail_features)
|
2020-03-16 19:38:41 +01:00
|
|
|
this.trail_features.clear();
|
2020-03-18 10:28:23 +01:00
|
|
|
if (this.trail_labels)
|
2020-03-16 19:38:41 +01:00
|
|
|
this.trail_labels.clear();
|
2020-03-18 10:28:23 +01:00
|
|
|
|
2020-03-27 08:45:56 +01:00
|
|
|
for (let i in this.track_linesegs) {
|
2020-01-03 21:16:40 +01:00
|
|
|
this.track_linesegs[i].feature = undefined;
|
|
|
|
|
this.track_linesegs[i].label = undefined;
|
|
|
|
|
}
|
|
|
|
|
this.elastic_feature = null;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
trailGroup.remove(this.layer);
|
|
|
|
|
|
|
|
|
|
this.trail_features = new ol.Collection();
|
|
|
|
|
|
|
|
|
|
this.layer = new ol.layer.Vector({
|
|
|
|
|
name: this.icao,
|
|
|
|
|
isTrail: true,
|
|
|
|
|
source: new ol.source.Vector({
|
|
|
|
|
features: this.trail_features,
|
|
|
|
|
}),
|
|
|
|
|
renderOrder: null,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
trailGroup.push(this.layer);
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-16 16:09:11 +01:00
|
|
|
PlaneObject.prototype.makeTR = function() {
|
|
|
|
|
|
|
|
|
|
this.tr = PlaneRowTemplate.cloneNode(true);
|
|
|
|
|
|
|
|
|
|
if (this.icao[0] === '~') {
|
|
|
|
|
// Non-ICAO address
|
|
|
|
|
this.tr.cells[0].textContent = this.icao.substring(1);
|
|
|
|
|
$(this.tr).css('font-style', 'italic');
|
|
|
|
|
} else {
|
|
|
|
|
this.tr.cells[0].textContent = this.icao;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// set flag image if available
|
|
|
|
|
if (ShowFlags && this.icaorange.flag_image !== null) {
|
|
|
|
|
$('img', this.tr.cells[1]).attr('src', FlagPath + this.icaorange.flag_image);
|
|
|
|
|
$('img', this.tr.cells[1]).attr('title', this.icaorange.country);
|
|
|
|
|
} else {
|
|
|
|
|
$('img', this.tr.cells[1]).css('display', 'none');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.clickListener = function(evt) {
|
|
|
|
|
if (evt.srcElement instanceof HTMLAnchorElement) {
|
|
|
|
|
evt.stopPropagation();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!mapIsVisible) {
|
|
|
|
|
selectPlaneByHex(this.icao, {follow: true});
|
|
|
|
|
//showMap();
|
|
|
|
|
} else {
|
|
|
|
|
selectPlaneByHex(this.icao, {follow: false});
|
|
|
|
|
}
|
|
|
|
|
evt.preventDefault();
|
|
|
|
|
}.bind(this);
|
|
|
|
|
|
|
|
|
|
if (!globeIndex) {
|
|
|
|
|
this.dblclickListener = function(evt) {
|
|
|
|
|
if(!mapIsVisible) {
|
|
|
|
|
showMap();
|
|
|
|
|
}
|
|
|
|
|
selectPlaneByHex(this.icao, {follow: true});
|
|
|
|
|
evt.preventDefault();
|
|
|
|
|
}.bind(this);
|
|
|
|
|
|
|
|
|
|
this.tr.addEventListener('dblclick', this.dblclickListener);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.tr.addEventListener('click', this.clickListener);
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-03 21:16:40 +01:00
|
|
|
PlaneObject.prototype.destroy = function() {
|
|
|
|
|
this.clearLines();
|
|
|
|
|
this.clearMarker();
|
|
|
|
|
this.visible = false;
|
2020-03-16 16:21:11 +01:00
|
|
|
if (this.layer) {
|
|
|
|
|
trailGroup.remove(this.layer);
|
|
|
|
|
this.trail_features.clear();
|
2020-03-16 18:12:59 +01:00
|
|
|
this.layer = null;
|
2020-03-18 10:28:23 +01:00
|
|
|
}
|
|
|
|
|
if (this.layer_labels) {
|
|
|
|
|
trailGroup.remove(this.layer_labels);
|
|
|
|
|
this.trail_labels.clear();
|
2020-03-16 18:12:59 +01:00
|
|
|
this.layer_labels = null;
|
2020-03-16 16:21:11 +01:00
|
|
|
}
|
2020-01-03 21:16:40 +01:00
|
|
|
if (this.tr) {
|
2020-03-27 08:45:56 +01:00
|
|
|
let tbody = document.getElementById('tableinfo').tBodies[0];
|
2020-03-16 12:21:02 +01:00
|
|
|
if (!this.inTable) {
|
|
|
|
|
tbody.appendChild(this.tr);
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-03 21:16:40 +01:00
|
|
|
this.tr.removeEventListener('click', this.clickListener);
|
|
|
|
|
this.tr.removeEventListener('dblclick', this.dblclickListener);
|
2020-03-16 12:21:02 +01:00
|
|
|
|
|
|
|
|
tbody.removeChild(this.tr);
|
|
|
|
|
|
2020-01-03 21:16:40 +01:00
|
|
|
if (this.tr.parentNode)
|
|
|
|
|
this.tr.parentNode.removeChild(this.tr);
|
|
|
|
|
this.tr = null;
|
|
|
|
|
}
|
|
|
|
|
if (this.icao == SelectedPlane)
|
|
|
|
|
SelectedPlane = null;
|
2020-03-27 08:45:56 +01:00
|
|
|
for (let key in Object.keys(this)) {
|
2020-01-03 21:16:40 +01:00
|
|
|
delete this[key];
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function calcAltitudeRounded(altitude) {
|
|
|
|
|
if (altitude == null) {
|
|
|
|
|
return null;
|
|
|
|
|
} else if (altitude == "ground") {
|
|
|
|
|
return altitude;
|
2020-05-18 16:12:25 +02:00
|
|
|
} else if (altitude > 8000 || heatmap) {
|
2020-01-03 21:16:40 +01:00
|
|
|
return (altitude/500).toFixed(0)*500;
|
2020-01-09 17:20:03 +01:00
|
|
|
} else {
|
|
|
|
|
return (altitude/125).toFixed(0)*125;
|
2020-01-03 21:16:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PlaneObject.prototype.drawRedDot = function(bad_position) {
|
2020-03-18 10:28:23 +01:00
|
|
|
this.checkLayers();
|
2020-01-03 21:16:40 +01:00
|
|
|
if (debugJump && loadFinished && SelectedPlane != this) {
|
|
|
|
|
OLMap.getView().setCenter(ol.proj.fromLonLat(bad_position));
|
|
|
|
|
selectPlaneByHex(this.icao, false);
|
|
|
|
|
}
|
2020-03-27 08:45:56 +01:00
|
|
|
let badFeat = new ol.Feature(new ol.geom.Point(ol.proj.fromLonLat(bad_position)));
|
2020-01-03 21:16:40 +01:00
|
|
|
badFeat.setStyle(this.dataSource == "mlat" ? badDotMlat : badDot);
|
2020-03-18 10:28:23 +01:00
|
|
|
this.trail_features.addFeature(badFeat);
|
2020-03-27 08:45:56 +01:00
|
|
|
let geom = new ol.geom.LineString([ol.proj.fromLonLat(this.prev_position), ol.proj.fromLonLat(bad_position)]);
|
|
|
|
|
let lineFeat = new ol.Feature(geom);
|
2020-01-03 21:16:40 +01:00
|
|
|
lineFeat.setStyle(this.dataSource == "mlat" ? badLineMlat : badLine);
|
2020-03-18 10:28:23 +01:00
|
|
|
this.trail_features.addFeature(lineFeat);
|
2020-01-03 21:16:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Converts an HSL color value to RGB. Conversion formula
|
|
|
|
|
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
|
|
|
|
|
* Assumes h, s, and l are contained in the set [0, 1] and
|
|
|
|
|
* returns r, g, and b in the set [0, 255].
|
|
|
|
|
*
|
|
|
|
|
* @param {number} h The hue
|
|
|
|
|
* @param {number} s The saturation
|
|
|
|
|
* @param {number} l The lightness
|
|
|
|
|
* @return {Array} The RGB representation
|
|
|
|
|
*/
|
2020-05-17 02:51:43 +02:00
|
|
|
function hslToRgb(arr, opacity){
|
2020-04-13 09:47:56 +02:00
|
|
|
let h = arr[0];
|
|
|
|
|
let s = arr[1];
|
|
|
|
|
let l = arr[2];
|
2020-03-27 08:45:56 +01:00
|
|
|
let r, g, b;
|
2020-01-03 21:16:40 +01:00
|
|
|
|
|
|
|
|
h /= 360;
|
|
|
|
|
s *= 0.01;
|
|
|
|
|
l *= 0.01;
|
|
|
|
|
|
|
|
|
|
if(s == 0){
|
|
|
|
|
r = g = b = l; // achromatic
|
|
|
|
|
}else{
|
2020-03-27 08:45:56 +01:00
|
|
|
let hue2rgb = function hue2rgb(p, q, t){
|
2020-01-03 21:16:40 +01:00
|
|
|
if(t < 0) t += 1;
|
|
|
|
|
if(t > 1) t -= 1;
|
|
|
|
|
if(t < 1/6) return p + (q - p) * 6 * t;
|
|
|
|
|
if(t < 1/2) return q;
|
|
|
|
|
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-27 08:45:56 +01:00
|
|
|
let q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
|
|
|
|
let p = 2 * l - q;
|
2020-01-03 21:16:40 +01:00
|
|
|
r = hue2rgb(p, q, h + 1/3);
|
|
|
|
|
g = hue2rgb(p, q, h);
|
|
|
|
|
b = hue2rgb(p, q, h - 1/3);
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-17 02:51:43 +02:00
|
|
|
if (opacity != null)
|
|
|
|
|
return 'rgba(' + Math.round(r * 255) + ', ' + Math.round(g * 255) + ', ' + Math.round(b * 255) + ', ' + opacity + ')';
|
|
|
|
|
else
|
|
|
|
|
return 'rgb(' + Math.round(r * 255) + ', ' + Math.round(g * 255) + ', ' + Math.round(b * 255) + ')';
|
2020-01-03 21:16:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PlaneObject.prototype.altBad = function(newAlt, oldAlt, oldTime, data) {
|
2020-03-27 08:45:56 +01:00
|
|
|
let max_fpm = 12000;
|
2020-01-03 21:16:40 +01:00
|
|
|
if (data.geom_rate != null)
|
|
|
|
|
max_fpm = 1.3*Math.abs(data.goem_rate) + 5000;
|
|
|
|
|
else if (data.baro_rate != null)
|
|
|
|
|
max_fpm = 1.3*Math.abs(data.baro_rate) + 5000;
|
|
|
|
|
|
|
|
|
|
const delta = Math.abs(newAlt - oldAlt);
|
|
|
|
|
const fpm = (delta < 800) ? 0 : (60 * delta / (now - oldTime + 2));
|
|
|
|
|
return fpm > max_fpm;
|
|
|
|
|
}
|
2020-10-26 15:33:04 -04:00
|
|
|
|
2020-01-03 21:16:40 +01:00
|
|
|
PlaneObject.prototype.getAircraftData = function() {
|
2020-03-27 08:45:56 +01:00
|
|
|
let req = getAircraftData(this.icao);
|
2020-03-02 20:23:21 +01:00
|
|
|
|
2020-01-03 21:16:40 +01:00
|
|
|
req.done(function(data) {
|
2020-10-22 19:56:09 +02:00
|
|
|
this.regLoaded = true;
|
2020-01-03 21:16:40 +01:00
|
|
|
if (data == null) {
|
|
|
|
|
//console.log(this.icao + ': Not found in database!');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (data == "strange") {
|
|
|
|
|
//console.log(this.icao + ': Database malfunction!');
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//console.log(this.icao + ': loaded!');
|
2020-01-07 14:31:25 +01:00
|
|
|
// format [r:0, t:1, f:2]
|
2020-01-03 21:16:40 +01:00
|
|
|
|
2020-01-07 14:31:25 +01:00
|
|
|
if (data[0]) {
|
2020-01-09 01:26:41 +01:00
|
|
|
this.registration = data[0];
|
2020-01-03 21:16:40 +01:00
|
|
|
}
|
|
|
|
|
|
2020-01-07 14:31:25 +01:00
|
|
|
if (data[1]) {
|
|
|
|
|
this.icaoType = data[1];
|
2020-10-20 21:31:42 +02:00
|
|
|
this.setTypeData();
|
2020-01-03 21:16:40 +01:00
|
|
|
}
|
|
|
|
|
|
2020-08-12 17:08:42 +02:00
|
|
|
if (data[3]) {
|
|
|
|
|
this.typeLong = data[3];
|
2020-01-03 21:16:40 +01:00
|
|
|
}
|
|
|
|
|
|
2020-01-07 14:31:25 +01:00
|
|
|
if (data[2]) {
|
|
|
|
|
this.military = (data[2][0] == '1');
|
|
|
|
|
this.interesting = (data[2][1] == '1');
|
2020-01-03 21:16:40 +01:00
|
|
|
}
|
|
|
|
|
if (this.selected) {
|
|
|
|
|
refreshSelected();
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-19 14:02:57 +01:00
|
|
|
this.updateMarker(true);
|
|
|
|
|
|
2020-01-03 21:16:40 +01:00
|
|
|
data = null;
|
|
|
|
|
}.bind(this));
|
|
|
|
|
|
|
|
|
|
req.fail(function(jqXHR,textStatus,errorThrown) {
|
|
|
|
|
if (textStatus == 'timeout')
|
|
|
|
|
this.getAircraftData();
|
|
|
|
|
else
|
|
|
|
|
console.log(this.icao + ': Database load error: ' + textStatus + ' at URL: ' + jqXHR.url);
|
|
|
|
|
}.bind(this));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PlaneObject.prototype.reapTrail = function() {
|
|
|
|
|
const oldSegs = this.track_linesegs;
|
|
|
|
|
this.track_linesegs = [];
|
|
|
|
|
this.history_size = 0;
|
2020-03-27 08:45:56 +01:00
|
|
|
for (let i in oldSegs) {
|
2020-01-03 21:16:40 +01:00
|
|
|
const seg = oldSegs[i];
|
|
|
|
|
if (seg.ts + tempTrailsTimeout > now) {
|
|
|
|
|
this.history_size += seg.fixed.getCoordinates().length;
|
|
|
|
|
this.track_linesegs.push(seg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (this.track_linesegs.length != oldSegs.length) {
|
|
|
|
|
this.remakeTrail();
|
2020-03-18 17:02:31 +01:00
|
|
|
this.updateTick(true);
|
2020-01-03 21:16:40 +01:00
|
|
|
}
|
|
|
|
|
}
|
2020-03-04 18:14:46 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
PlaneObject.prototype.milRange = function() {
|
2020-10-23 01:37:32 +02:00
|
|
|
if (this.icao[0] == '~')
|
|
|
|
|
return false;
|
|
|
|
|
let i = parseInt(this.icao, 16);
|
2020-03-04 18:14:46 +01:00
|
|
|
return (
|
|
|
|
|
false
|
|
|
|
|
// us military
|
|
|
|
|
//adf7c8-adf7cf = united states mil_5(uf)
|
|
|
|
|
//adf7d0-adf7df = united states mil_4(uf)
|
|
|
|
|
//adf7e0-adf7ff = united states mil_3(uf)
|
|
|
|
|
//adf800-adffff = united states mil_2(uf)
|
|
|
|
|
//ae0000-afffff = united states mil_1(uf)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0xadf7c8 && i <= 0xafffff)
|
2020-03-04 18:14:46 +01:00
|
|
|
|
|
|
|
|
//010070-01008f = egypt_mil
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0x010070 && i <= 0x01008f)
|
2020-03-04 18:14:46 +01:00
|
|
|
|
|
|
|
|
//0a4000-0a4fff = algeria mil(ap)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0x0a4000 && i <= 0x0a4fff)
|
2020-03-04 18:14:46 +01:00
|
|
|
|
|
|
|
|
//33ff00-33ffff = italy mil(iy)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0x33ff00 && i <= 0x33ffff)
|
2020-03-04 18:14:46 +01:00
|
|
|
|
2020-08-14 14:45:25 +02:00
|
|
|
//350000-37ffff = spain mil(sp)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0x350000 && i <= 0x37ffff)
|
2020-03-04 18:14:46 +01:00
|
|
|
|
|
|
|
|
//3a8000-3affff = france mil_1(fs)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0x3a8000 && i <= 0x3affff)
|
2020-03-04 18:14:46 +01:00
|
|
|
//3b0000-3bffff = france mil_2(fs)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0x3b0000 && i <= 0x3bffff)
|
2020-03-04 18:14:46 +01:00
|
|
|
|
2020-10-23 01:37:32 +02:00
|
|
|
//3ea000-3ebfff = germany mil_1(df)
|
|
|
|
|
|| (i >= 0x3ea000 && i <= 0x3ebfff)
|
2020-03-04 18:14:46 +01:00
|
|
|
//3f4000-3f7fff = germany mil_2(df)
|
|
|
|
|
//3f8000-3fbfff = germany mil_3(df)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0x3f4000 && i <= 0x3fbfff)
|
2020-03-04 18:14:46 +01:00
|
|
|
|
|
|
|
|
//400000-40003f = united kingdom mil_1(ra)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0x400000 && i <= 0x40003f)
|
2020-05-16 14:03:49 +02:00
|
|
|
//43c000-43cfff = united kingdom mil(ra)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0x43c000 && i <= 0x43cfff)
|
2020-03-04 18:14:46 +01:00
|
|
|
|
2020-10-23 01:37:32 +02:00
|
|
|
//444000-446fff = austria mil(aq)
|
|
|
|
|
|| (i >= 0x444000 && i <= 0x446fff)
|
2020-03-04 18:14:46 +01:00
|
|
|
|
|
|
|
|
//44f000-44ffff = belgium mil(bc)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0x44f000 && i <= 0x44ffff)
|
2020-03-04 18:14:46 +01:00
|
|
|
|
|
|
|
|
//457000-457fff = bulgaria mil(bu)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0x457000 && i <= 0x457fff)
|
2020-03-04 18:14:46 +01:00
|
|
|
|
|
|
|
|
//45f400-45f4ff = denmark mil(dg)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0x45f400 && i <= 0x45f4ff)
|
2020-03-04 18:14:46 +01:00
|
|
|
|
|
|
|
|
//468000-4683ff = greece mil(gc)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0x468000 && i <= 0x4683ff)
|
2020-03-04 18:14:46 +01:00
|
|
|
|
|
|
|
|
//473c00-473c0f = hungary mil(hm)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0x473c00 && i <= 0x473c0f)
|
2020-03-04 18:14:46 +01:00
|
|
|
|
|
|
|
|
//478100-4781ff = norway mil(nn)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0x478100 && i <= 0x4781ff)
|
2020-03-04 18:14:46 +01:00
|
|
|
//480000-480fff = netherlands mil(nm)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0x480000 && i <= 0x480fff)
|
2020-03-04 18:14:46 +01:00
|
|
|
//48d800-48d87f = poland mil(po)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0x48d800 && i <= 0x48d87f)
|
2020-03-04 18:14:46 +01:00
|
|
|
//497c00-497cff = portugal mil(pu)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0x497c00 && i <= 0x497cff)
|
2020-03-04 18:14:46 +01:00
|
|
|
//498420-49842f = czech republic mil(ct)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0x498420 && i <= 0x49842f)
|
2020-03-04 18:14:46 +01:00
|
|
|
|
|
|
|
|
//4b7000-4b7fff = switzerland mil(su)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0x4b7000 && i <= 0x4b7fff)
|
2020-03-04 18:14:46 +01:00
|
|
|
//4b8200-4b82ff = turkey mil(tq)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0x4b8200 && i <= 0x4b82ff)
|
2020-03-04 18:14:46 +01:00
|
|
|
|
|
|
|
|
//506f00-506fff = slovenia mil(sj)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0x506f00 && i <= 0x506fff)
|
2020-03-04 18:14:46 +01:00
|
|
|
|
|
|
|
|
//70c070-70c07f = oman mil(on)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0x70c070 && i <= 0x70c07f)
|
2020-03-04 18:14:46 +01:00
|
|
|
|
|
|
|
|
//710258-71025f = saudi arabia mil_1(sx)
|
|
|
|
|
//710260-71027f = saudi arabia mil_2(sx)
|
|
|
|
|
//710280-71028f = saudi arabia mil_3(sx)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0x710258 && i <= 0x71028f)
|
2020-03-04 18:14:46 +01:00
|
|
|
//710380-71039f = saudi arabia mil_4(sx)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0x710380 && i <= 0x71039f)
|
2020-03-04 18:14:46 +01:00
|
|
|
|
|
|
|
|
//738a00-738aff = israel mil(iz)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0x738a00 && i <= 0x738aff)
|
|
|
|
|
|
|
|
|
|
//7c822e-7c84ff = australia mil_1(av)
|
|
|
|
|
|| (i >= 0x7c822e && i <= 0x7c84ff)
|
2020-03-04 18:14:46 +01:00
|
|
|
//7c8800-7c8fff = australia mil_7(av)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0x7c8800 && i <= 0x7c88ff)
|
2020-03-04 18:14:46 +01:00
|
|
|
//7c9000-7c9fff = australia mil_8(av)
|
|
|
|
|
//7ca000-7cbfff = australia mil_9(av)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0x7c9000 && i <= 0x7cbfff)
|
2020-03-04 18:14:46 +01:00
|
|
|
//7d0000-7dffff = australia mil_11(av)
|
|
|
|
|
//7e0000-7fffff = australia mil_12(av)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0x7d0000 && i <= 0x7fffff)
|
2020-03-04 18:14:46 +01:00
|
|
|
|
|
|
|
|
//800200-8002ff = india mil(im)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0x800200 && i <= 0x8002ff)
|
2020-03-04 18:14:46 +01:00
|
|
|
|
|
|
|
|
//c20000-c3ffff = canada mil(cb)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0xc20000 && i <= 0xc3ffff)
|
2020-03-04 18:14:46 +01:00
|
|
|
|
|
|
|
|
//e40000-e41fff = brazil mil(bq)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0xe40000 && i <= 0xe41fff)
|
2020-03-04 18:14:46 +01:00
|
|
|
|
|
|
|
|
//e80600-e806ff = chile mil(cq)
|
2020-10-23 01:37:32 +02:00
|
|
|
|| (i >= 0xe80600 && i <= 0xe806ff)
|
2020-03-04 18:14:46 +01:00
|
|
|
);
|
|
|
|
|
}
|
2020-04-18 21:10:16 +02:00
|
|
|
|
|
|
|
|
PlaneObject.prototype.updateTraceData = function(state, _now) {
|
|
|
|
|
const lat = state[1];
|
|
|
|
|
const lon = state[2];
|
|
|
|
|
const altitude = state[3];
|
|
|
|
|
const gs = state[4];
|
|
|
|
|
const track = state[5];
|
|
|
|
|
const rate_geom = state[6] & 4;
|
|
|
|
|
const alt_geom = state[6] & 8;
|
|
|
|
|
const rate = state[7];
|
|
|
|
|
const data = state[8];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.position = [lon, lat];
|
|
|
|
|
this.position_time = _now;
|
|
|
|
|
this.last_message_time = _now;
|
|
|
|
|
this.altitude = altitude;
|
|
|
|
|
this.alt_rounded = calcAltitudeRounded(this.altitude);
|
|
|
|
|
if (alt_geom) {
|
|
|
|
|
this.alt_geom = altitude;
|
2020-04-21 04:49:41 +02:00
|
|
|
//this.alt_baro = null;
|
2020-04-18 21:10:16 +02:00
|
|
|
} else {
|
|
|
|
|
this.alt_baro = altitude;
|
2020-04-21 04:49:41 +02:00
|
|
|
//this.alt_geom = null;
|
2020-04-18 21:10:16 +02:00
|
|
|
}
|
|
|
|
|
this.speed = gs;
|
|
|
|
|
this.gs = gs;
|
|
|
|
|
if (altitude == 'ground') {
|
|
|
|
|
this.true_heading = track;
|
|
|
|
|
this.track = null;
|
|
|
|
|
} else {
|
|
|
|
|
this.track = track;
|
2020-04-22 08:53:27 +02:00
|
|
|
//this.true_heading = null;
|
2020-04-18 21:10:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (track)
|
|
|
|
|
this.rotation = track
|
|
|
|
|
|
2020-04-26 19:28:44 +02:00
|
|
|
this.vert_rate = rate;
|
2020-04-18 21:10:16 +02:00
|
|
|
if (rate_geom) {
|
|
|
|
|
this.geom_rate = rate;
|
|
|
|
|
this.baro_rate = null;
|
|
|
|
|
} else {
|
|
|
|
|
this.baro_rate = rate;
|
|
|
|
|
this.geom_rate = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (data != null) {
|
|
|
|
|
if (data.type.substring(0,4) == "adsb" || data.type.substring(0,4) == "adsr") {
|
|
|
|
|
this.dataSource = "adsb";
|
|
|
|
|
} else if (data.type == "mlat") {
|
|
|
|
|
this.dataSource = "mlat";
|
|
|
|
|
} else if (data.type == "adsb_icao_nt") {
|
2020-04-21 03:06:33 +02:00
|
|
|
this.dataSource = "mode_s";
|
2020-04-18 21:10:16 +02:00
|
|
|
} else if (data.type.substring(0,4) == "tisb") {
|
|
|
|
|
this.dataSource = "tisb";
|
|
|
|
|
} else if (data.type == 'adsc') {
|
2020-04-21 03:39:28 +02:00
|
|
|
this.dataSource = "adsc";
|
2020-04-18 21:10:16 +02:00
|
|
|
} else if (data.type == 'unknown') {
|
|
|
|
|
this.dataSource = "unknown";
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-21 20:09:05 +02:00
|
|
|
if (data.flight != null) {
|
|
|
|
|
this.flight = data.flight;
|
|
|
|
|
this.name = data.flight.trim();
|
|
|
|
|
}
|
2020-04-18 21:10:16 +02:00
|
|
|
this.addrtype = data.type;
|
|
|
|
|
|
|
|
|
|
this.alt_geom = data.alt_geom;
|
|
|
|
|
this.ias = data.ias;
|
|
|
|
|
this.tas = data.tas;
|
2020-04-21 04:49:41 +02:00
|
|
|
this.track = data.track;
|
2020-04-18 21:10:16 +02:00
|
|
|
this.mag_heading = data.mag_heading;
|
2020-04-21 04:49:41 +02:00
|
|
|
this.true_heading = data.true_heading;
|
2020-04-18 21:10:16 +02:00
|
|
|
this.mach = data.mach;
|
2020-04-21 04:49:41 +02:00
|
|
|
this.track_rate = data.track_rate;
|
2020-04-18 21:10:16 +02:00
|
|
|
this.roll = data.roll;
|
|
|
|
|
this.nav_altitude = data.nav_altitude;
|
|
|
|
|
this.nav_heading = data.nav_heading;
|
|
|
|
|
this.nav_modes = data.nav_modes;
|
|
|
|
|
this.nac_p = data.nac_p;
|
|
|
|
|
this.nac_v = data.nac_v;
|
|
|
|
|
this.nic_baro = data.nic_baro;
|
|
|
|
|
this.sil_type = data.sil_type;
|
|
|
|
|
this.sil = data.sil;
|
|
|
|
|
this.nav_qnh = data.nav_qnh;
|
|
|
|
|
this.baro_rate = data.baro_rate;
|
|
|
|
|
this.geom_rate = data.geom_rate;
|
|
|
|
|
this.rc = data.rc;
|
|
|
|
|
this.squawk = data.squawk;
|
|
|
|
|
|
2020-04-22 13:35:05 +02:00
|
|
|
this.wd = data.wd;
|
|
|
|
|
this.ws = data.ws;
|
|
|
|
|
this.oat = data.oat;
|
|
|
|
|
this.tat = data.tat;
|
|
|
|
|
|
2020-04-18 21:10:16 +02:00
|
|
|
// fields with more complex behaviour
|
|
|
|
|
|
|
|
|
|
this.version = data.version;
|
|
|
|
|
if (data.category != null) {
|
|
|
|
|
this.category = data.category;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (data.nav_altitude_fms != null) {
|
|
|
|
|
this.nav_altitude = data.nav_altitude_fms;
|
|
|
|
|
} else if (data.nav_altitude_mcp != null){
|
|
|
|
|
this.nav_altitude = data.nav_altitude_mcp;
|
|
|
|
|
} else {
|
|
|
|
|
this.nav_altitude = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-19 22:19:31 +02:00
|
|
|
|
|
|
|
|
PlaneObject.prototype.setNull = function() {
|
|
|
|
|
this.position = null;
|
|
|
|
|
this.callsign = null;
|
|
|
|
|
this.track = null;
|
|
|
|
|
this.rotation = null;
|
|
|
|
|
this.altitude = null;
|
|
|
|
|
this.messages = NaN;
|
|
|
|
|
this.seen = NaN;
|
|
|
|
|
this.last_message_time = NaN;
|
|
|
|
|
this.seen_pos = NaN;
|
|
|
|
|
this.position_time = NaN;
|
|
|
|
|
|
|
|
|
|
this.flight = null;
|
|
|
|
|
this.squawk = null;
|
|
|
|
|
this.altitude = null;
|
|
|
|
|
this.alt_baro = null;
|
|
|
|
|
this.alt_geom = null;
|
|
|
|
|
this.altitudeTime = 0;
|
|
|
|
|
this.bad_alt = null;
|
|
|
|
|
this.bad_altTime = null;
|
|
|
|
|
this.alt_reliable = 0;
|
|
|
|
|
|
|
|
|
|
this.speed = null;
|
|
|
|
|
this.gs = null;
|
|
|
|
|
this.ias = null;
|
|
|
|
|
this.tas = null;
|
|
|
|
|
|
|
|
|
|
this.track = null;
|
|
|
|
|
this.track_rate = null;
|
|
|
|
|
this.mag_heading = null;
|
|
|
|
|
this.true_heading = null;
|
|
|
|
|
this.mach = null;
|
|
|
|
|
this.roll = null;
|
|
|
|
|
this.nav_altitude = null;
|
|
|
|
|
this.nav_heading = null;
|
|
|
|
|
this.nav_modes = null;
|
|
|
|
|
this.nav_qnh = null;
|
|
|
|
|
this.rc = null;
|
|
|
|
|
|
|
|
|
|
this.rotation = 0;
|
|
|
|
|
|
|
|
|
|
this.nac_p = null;
|
|
|
|
|
this.nac_v = null;
|
|
|
|
|
this.nic_baro = null;
|
|
|
|
|
this.sil_type = null;
|
|
|
|
|
this.sil = null;
|
|
|
|
|
|
|
|
|
|
this.baro_rate = null;
|
|
|
|
|
this.geom_rate = null;
|
|
|
|
|
this.vert_rate = null;
|
|
|
|
|
|
2020-04-22 08:53:27 +02:00
|
|
|
this.wd = null;
|
|
|
|
|
this.ws = null;
|
|
|
|
|
this.oat = null;
|
|
|
|
|
this.tat = null;
|
|
|
|
|
|
2020-04-19 22:19:31 +02:00
|
|
|
this.version = null;
|
|
|
|
|
|
|
|
|
|
this.prev_position = null;
|
|
|
|
|
this.prev_time = null;
|
|
|
|
|
this.prev_track = null;
|
|
|
|
|
this.position = null;
|
|
|
|
|
this.sitedist = null;
|
|
|
|
|
this.too_fast = 0;
|
|
|
|
|
|
|
|
|
|
this.messages = 0;
|
|
|
|
|
this.rssi = null;
|
|
|
|
|
this.msgs1090 = 0;
|
|
|
|
|
this.msgs978 = 0;
|
|
|
|
|
this.messageRate = 0;
|
|
|
|
|
this.messageRateOld = 0;
|
|
|
|
|
}
|
2020-04-23 09:57:31 +02:00
|
|
|
|
|
|
|
|
function makeCircle(points, greyskull) {
|
|
|
|
|
let out = points;
|
|
|
|
|
//console.log('1: ' + out.map(x => [Math.round(x[0]), Math.round(x[1])]));
|
|
|
|
|
for (let k = 0; k < greyskull; k++) {
|
|
|
|
|
out = [points[0]];
|
|
|
|
|
for (let j = 1; j < points.length; j++) {
|
|
|
|
|
let i = j - 1;
|
|
|
|
|
out.push(midpoint(points[i], points[j]));
|
|
|
|
|
out.push(points[j]);
|
|
|
|
|
//console.log('added 2: ' + out.map(x => [Math.round(x[0]), Math.round(x[1])]));
|
|
|
|
|
}
|
|
|
|
|
points = out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-23 10:23:58 +02:00
|
|
|
// adapted from https://github.com/seangrogan/great_circle_calculator
|
2020-04-23 09:57:31 +02:00
|
|
|
function midpoint(from, to) {
|
2020-04-23 10:23:58 +02:00
|
|
|
let lon1 = from[0] * (Math.PI/180);
|
|
|
|
|
let lat1 = from[1] * (Math.PI/180);
|
|
|
|
|
let lon2 = to[0] * (Math.PI/180);
|
|
|
|
|
let lat2 = to[1] * (Math.PI/180);
|
2020-04-23 09:57:31 +02:00
|
|
|
|
|
|
|
|
let b_x = Math.cos(lat2) * Math.cos(lon2 - lon1);
|
|
|
|
|
let b_y = Math.cos(lat2) * Math.sin(lon2 - lon1);
|
|
|
|
|
let lat3 = Math.atan2(Math.sin(lat1) + Math.sin(lat2), Math.sqrt((Math.cos(lat1) + b_x) * (Math.cos(lat1) + b_x) + b_y * b_y));
|
|
|
|
|
let lon3 = lon1 + Math.atan2(b_y, Math.cos(lat1) + b_x);
|
|
|
|
|
lat3 /= (Math.PI/180);
|
|
|
|
|
lon3 /= (Math.PI/180);
|
|
|
|
|
lon3 = (lon3 + 540) % 360 - 180;
|
|
|
|
|
return [lon3, lat3];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PlaneObject.prototype.cross180 = function(on_ground, is_leg) {
|
|
|
|
|
let sign1 = Math.sign(this.prev_position[0]);
|
|
|
|
|
let sign2 = Math.sign(this.position[0]);
|
|
|
|
|
|
|
|
|
|
let out = makeCircle([this.prev_position, this.position], 8);
|
|
|
|
|
|
|
|
|
|
//console.log([...out]);
|
|
|
|
|
|
|
|
|
|
let seg1 = [];
|
|
|
|
|
let seg2 = [];
|
|
|
|
|
|
|
|
|
|
let tmp;
|
|
|
|
|
|
|
|
|
|
while ((tmp = out.shift()) != null) {
|
|
|
|
|
if (sign1 == Math.sign(tmp[0]))
|
|
|
|
|
seg1.push(tmp);
|
|
|
|
|
else
|
|
|
|
|
seg2.push(tmp);
|
|
|
|
|
}
|
|
|
|
|
//console.log([...seg1]);
|
|
|
|
|
//console.log([...seg2]);
|
2020-04-27 18:14:22 +02:00
|
|
|
let before = seg1[seg1.length - 1];
|
|
|
|
|
let after = seg2[0];
|
|
|
|
|
// weight according to the opposite distance, well longitude difference
|
|
|
|
|
// not perfect, good enough
|
|
|
|
|
let afterWeight = Math.abs(sign1 * 180 - before[0]);
|
|
|
|
|
let beforeWeight = Math.abs(sign2 * 180 - after[0]);
|
|
|
|
|
let midLat = (beforeWeight * before[1] + afterWeight * after[1]) / (beforeWeight + afterWeight);
|
2020-04-23 09:57:31 +02:00
|
|
|
|
|
|
|
|
let midPoint1 = [sign1 * 180, midLat];
|
|
|
|
|
let midPoint2 = [sign2 * 180, midLat];
|
|
|
|
|
|
|
|
|
|
seg1.push(midPoint1);
|
|
|
|
|
seg2.unshift(midPoint2);
|
|
|
|
|
|
|
|
|
|
for (let i in seg1)
|
|
|
|
|
seg1[i] = ol.proj.fromLonLat(seg1[i]);
|
|
|
|
|
for (let i in seg2)
|
|
|
|
|
seg2[i] = ol.proj.fromLonLat(seg2[i]);
|
|
|
|
|
|
|
|
|
|
seg1.unshift(seg1[0]);
|
|
|
|
|
|
|
|
|
|
this.track_linesegs.push({ fixed: new ol.geom.LineString([seg1.shift()]),
|
|
|
|
|
feature: null,
|
|
|
|
|
estimated: true,
|
|
|
|
|
altitude: this.prev_alt_rounded,
|
|
|
|
|
alt_real: this.prev_alt,
|
|
|
|
|
speed: this.prev_speed,
|
|
|
|
|
ground: on_ground,
|
|
|
|
|
ts: this.prev_time,
|
|
|
|
|
track: this.prev_rot,
|
|
|
|
|
leg: is_leg,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.track_linesegs.push({ fixed: new ol.geom.LineString(seg1),
|
|
|
|
|
feature: null,
|
|
|
|
|
estimated: true,
|
|
|
|
|
altitude: this.prev_alt_rounded,
|
|
|
|
|
alt_real: this.prev_alt,
|
|
|
|
|
speed: this.prev_speed,
|
|
|
|
|
ground: on_ground,
|
|
|
|
|
track: this.prev_rot,
|
|
|
|
|
ts: NaN,
|
|
|
|
|
noLabel: true,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
this.track_linesegs.push({ fixed: new ol.geom.LineString(seg2),
|
|
|
|
|
feature: null,
|
|
|
|
|
estimated: true,
|
|
|
|
|
altitude: this.prev_alt_rounded,
|
|
|
|
|
alt_real: this.prev_alt,
|
|
|
|
|
speed: this.prev_speed,
|
|
|
|
|
ground: on_ground,
|
|
|
|
|
track: this.prev_rot,
|
|
|
|
|
ts: NaN,
|
|
|
|
|
noLabel: true,
|
|
|
|
|
});
|
|
|
|
|
}
|
2020-10-26 15:33:04 -04:00
|
|
|
|
2020-06-08 21:10:35 +02:00
|
|
|
PlaneObject.prototype.isNonIcao = function() {
|
|
|
|
|
if (this.icao[0] == '~')
|
|
|
|
|
return true;
|
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2020-09-26 18:03:24 +02:00
|
|
|
|
|
|
|
|
PlaneObject.prototype.checkVisible = function() {
|
|
|
|
|
const zoomedOut = 40 * Math.max(0, 7 - ZoomLvl);
|
|
|
|
|
const jaeroTime = (this.dataSource == "adsc") ? 35*60 : 0;
|
|
|
|
|
const mlatTime = (this.dataSource == "mlat") ? 25 : 0;
|
|
|
|
|
const tisbReduction = (this.icao[0] == '~') ? 15 : 0;
|
|
|
|
|
// If no packet in over 58 seconds, clear the plane.
|
|
|
|
|
// Only clear the plane if it's not selected individually
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
(!globeIndex && this.seen < (58 - tisbReduction + jaeroTime))
|
|
|
|
|
|| (globeIndex && this.seen_pos < inactive / 100 * (40 + zoomedOut + jaeroTime + mlatTime - tisbReduction))
|
|
|
|
|
|| (this.selected && (onlySelected || (!SelectedAllPlanes && !multiSelect)))
|
|
|
|
|
|| noVanish
|
|
|
|
|
);
|
|
|
|
|
}
|
2020-10-20 21:31:42 +02:00
|
|
|
|
|
|
|
|
PlaneObject.prototype.setTypeData = function() {
|
|
|
|
|
if (_aircraft_type_cache == null || !this.icaoType || this.icaoType == this.icaoTypeCache)
|
|
|
|
|
return;
|
|
|
|
|
this.icaoTypeCache = this.icaoType;
|
|
|
|
|
|
|
|
|
|
let typeDesignator = this.icaoType.toUpperCase();
|
|
|
|
|
if (!(typeDesignator in _aircraft_type_cache))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
let typeData = _aircraft_type_cache[typeDesignator];
|
|
|
|
|
if (typeData.desc != null && typeData.desc.length == 3)
|
|
|
|
|
this.typeDescription = typeData.desc;
|
|
|
|
|
if (typeData.wtc != null)
|
|
|
|
|
this.wtc = typeData.wtc;
|
|
|
|
|
}
|
2020-10-26 15:33:04 -04:00
|
|
|
|
2020-10-21 17:16:39 +02:00
|
|
|
PlaneObject.prototype.checkForDB = function(t) {
|
2020-10-22 19:56:09 +02:00
|
|
|
if (!t) {
|
2020-10-21 17:16:39 +02:00
|
|
|
return;
|
2020-10-22 19:56:09 +02:00
|
|
|
}
|
2020-10-21 17:16:39 +02:00
|
|
|
|
|
|
|
|
if (t.desc) this.typeLong = t.desc;
|
|
|
|
|
if (t.r) this.registration = t.r;
|
2020-10-22 19:56:09 +02:00
|
|
|
|
|
|
|
|
if (!t.r && !dbServer && !this.regLoaded)
|
|
|
|
|
this.getAircraftData();
|
|
|
|
|
|
2020-10-21 17:16:39 +02:00
|
|
|
if (t.t) {
|
|
|
|
|
this.icaoType = t.t;
|
|
|
|
|
this.setTypeData();
|
|
|
|
|
}
|
|
|
|
|
if (t.dbFlags) {
|
|
|
|
|
this.military = t.dbFlags & 1;
|
|
|
|
|
this.interesting = t.dbFlags & 2;
|
|
|
|
|
}
|
|
|
|
|
}
|