2015-01-07 18:35:32 +01:00
|
|
|
"use strict";
|
|
|
|
|
2015-01-07 18:18:33 +01:00
|
|
|
function PlaneObject(icao) {
|
|
|
|
// Info about the plane
|
|
|
|
this.icao = icao;
|
2015-09-01 13:19:23 +02:00
|
|
|
this.icaorange = findICAORange(icao);
|
2015-01-07 18:18:33 +01:00
|
|
|
this.flight = null;
|
2018-08-17 18:31:12 +02:00
|
|
|
this.squawk = null;
|
|
|
|
this.selected = false;
|
2015-02-22 21:11:11 +01:00
|
|
|
this.category = null;
|
2015-01-07 02:19:05 +01:00
|
|
|
|
2013-05-24 04:15:37 +02:00
|
|
|
// Basic location information
|
2017-06-15 23:36:23 +02:00
|
|
|
this.altitude = null;
|
2018-03-08 18:34:34 +01:00
|
|
|
this.alt_baro = null;
|
2017-06-15 23:36:23 +02:00
|
|
|
this.alt_geom = null;
|
2018-03-08 18:34:34 +01:00
|
|
|
|
|
|
|
this.speed = null;
|
2017-06-15 23:36:23 +02:00
|
|
|
this.gs = null;
|
|
|
|
this.ias = null;
|
|
|
|
this.tas = null;
|
2018-03-08 18:34:34 +01:00
|
|
|
|
2017-06-15 23:36:23 +02:00
|
|
|
this.track = null;
|
|
|
|
this.track_rate = null;
|
|
|
|
this.mag_heading = null;
|
|
|
|
this.true_heading = null;
|
|
|
|
this.mach = null;
|
|
|
|
this.roll = null;
|
2018-03-08 22:41:29 +01:00
|
|
|
this.nav_altitude = null;
|
2018-03-08 18:34:34 +01:00
|
|
|
this.nav_heading = null;
|
|
|
|
this.nav_modes = null;
|
2018-08-17 18:33:35 +02:00
|
|
|
this.nav_qnh = null;
|
2018-08-17 18:31:12 +02:00
|
|
|
this.rc = null;
|
2018-07-31 19:17:27 +02:00
|
|
|
|
2018-08-17 18:31:12 +02:00
|
|
|
this.nac_p = null;
|
|
|
|
this.nac_v = null;
|
|
|
|
this.nic_baro = null;
|
|
|
|
this.sil_type = null;
|
|
|
|
this.sil = null;
|
2018-03-08 18:34:34 +01:00
|
|
|
|
2017-06-15 23:36:23 +02:00
|
|
|
this.baro_rate = null;
|
|
|
|
this.geom_rate = null;
|
2018-03-08 18:34:34 +01:00
|
|
|
this.vert_rate = null;
|
|
|
|
|
2017-06-16 00:42:05 +02:00
|
|
|
this.version = null;
|
2017-06-15 23:36:23 +02:00
|
|
|
|
2016-07-02 15:44:14 +02:00
|
|
|
this.prev_position = null;
|
2015-01-07 18:18:33 +01:00
|
|
|
this.position = null;
|
2015-06-29 16:51:42 +02:00
|
|
|
this.position_from_mlat = false
|
2015-01-07 18:18:33 +01:00
|
|
|
this.sitedist = null;
|
2013-05-24 04:15:37 +02:00
|
|
|
|
|
|
|
// Data packet numbers
|
2015-01-07 18:18:33 +01:00
|
|
|
this.messages = null;
|
2015-01-22 16:31:10 +01:00
|
|
|
this.rssi = null;
|
2013-05-24 04:15:37 +02:00
|
|
|
|
2015-01-07 17:32:58 +01:00
|
|
|
// Track history as a series of line segments
|
2016-09-15 13:37:30 +02:00
|
|
|
this.elastic_feature = null;
|
2015-01-07 18:18:33 +01:00
|
|
|
this.track_linesegs = [];
|
|
|
|
this.history_size = 0;
|
|
|
|
|
2015-01-07 02:19:05 +01:00
|
|
|
// When was this last updated (receiver timestamp)
|
2015-01-07 18:18:33 +01:00
|
|
|
this.last_message_time = null;
|
|
|
|
this.last_position_time = null;
|
2015-01-07 02:19:05 +01:00
|
|
|
|
2015-01-07 17:32:58 +01:00
|
|
|
// When was this last updated (seconds before last update)
|
2015-01-07 18:18:33 +01:00
|
|
|
this.seen = null;
|
|
|
|
this.seen_pos = null;
|
2015-01-07 02:19:05 +01:00
|
|
|
|
2015-01-07 18:18:33 +01:00
|
|
|
// Display info
|
|
|
|
this.visible = true;
|
|
|
|
this.marker = null;
|
2016-07-02 18:10:52 +02:00
|
|
|
this.markerStyle = null;
|
|
|
|
this.markerIcon = null;
|
2016-08-29 13:38:00 +02:00
|
|
|
this.markerStaticStyle = null;
|
|
|
|
this.markerStaticIcon = null;
|
2016-07-02 18:10:52 +02:00
|
|
|
this.markerStyleKey = null;
|
|
|
|
this.markerSvgKey = null;
|
2016-08-24 22:28:13 +02:00
|
|
|
this.filter = {};
|
2015-02-24 22:51:30 +01:00
|
|
|
|
2016-09-09 18:16:17 +02:00
|
|
|
// start from a computed registration, let the DB override it
|
|
|
|
// if it has something else.
|
|
|
|
this.registration = registration_from_hexid(this.icao);
|
2015-02-24 22:51:30 +01:00
|
|
|
this.icaotype = null;
|
2016-08-30 00:12:52 +02:00
|
|
|
this.typeDescription = null;
|
2016-08-29 17:07:03 +02:00
|
|
|
this.wtc = null;
|
2016-09-09 18:16:17 +02:00
|
|
|
|
|
|
|
// request metadata
|
2015-02-24 22:51:30 +01:00
|
|
|
getAircraftData(this.icao).done(function(data) {
|
|
|
|
if ("r" in data) {
|
|
|
|
this.registration = data.r;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ("t" in data) {
|
|
|
|
this.icaotype = data.t;
|
|
|
|
}
|
|
|
|
|
2016-08-29 17:07:03 +02:00
|
|
|
if ("desc" in data) {
|
2016-08-30 00:12:52 +02:00
|
|
|
this.typeDescription = data.desc;
|
2016-08-29 17:07:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if ("wtc" in data) {
|
2016-08-30 00:12:52 +02:00
|
|
|
this.wtc = data.wtc;
|
2016-08-29 17:07:03 +02:00
|
|
|
}
|
|
|
|
|
2015-02-24 22:51:30 +01:00
|
|
|
if (this.selected) {
|
|
|
|
refreshSelected();
|
|
|
|
}
|
|
|
|
}.bind(this));
|
2015-01-07 18:18:33 +01:00
|
|
|
}
|
2013-05-24 04:15:37 +02:00
|
|
|
|
2016-08-24 22:28:13 +02:00
|
|
|
PlaneObject.prototype.isFiltered = function() {
|
|
|
|
if (this.filter.minAltitude !== undefined && this.filter.maxAltitude !== undefined) {
|
|
|
|
if (this.altitude === null || this.altitude === undefined) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
var planeAltitude = this.altitude === "ground" ? 0 : convert_altitude(this.altitude, this.filter.altitudeUnits);
|
|
|
|
return planeAltitude < this.filter.minAltitude || planeAltitude > this.filter.maxAltitude;
|
|
|
|
}
|
|
|
|
|
2017-01-26 20:38:47 +01:00
|
|
|
// filter out ground vehicles
|
|
|
|
if (typeof this.filter.groundVehicles !== 'undefined' && this.filter.groundVehicles === 'filtered') {
|
|
|
|
if (typeof this.category === 'string' && this.category.startsWith('C')) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// filter out blocked MLAT flights
|
|
|
|
if (typeof this.filter.blockedMLAT !== 'undefined' && this.filter.blockedMLAT === 'filtered') {
|
|
|
|
if (typeof this.icao === 'string' && this.icao.startsWith('~')) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-24 22:28:13 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-01-07 18:18:33 +01:00
|
|
|
// Appends data to the running track so we can get a visual tail on the plane
|
|
|
|
// Only useful for a long running browser session.
|
2015-01-15 22:01:14 +01:00
|
|
|
PlaneObject.prototype.updateTrack = function(estimate_time) {
|
2016-07-02 15:44:14 +02:00
|
|
|
if (!this.position)
|
|
|
|
return false;
|
|
|
|
if (this.position == this.prev_position)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
var projHere = ol.proj.fromLonLat(this.position);
|
|
|
|
var projPrev;
|
|
|
|
if (this.prev_position === null) {
|
|
|
|
projPrev = projHere;
|
|
|
|
} else {
|
|
|
|
projPrev = ol.proj.fromLonLat(this.prev_position);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.prev_position = this.position;
|
2015-01-06 21:15:25 +01:00
|
|
|
|
2015-01-07 18:18:33 +01:00
|
|
|
if (this.track_linesegs.length == 0) {
|
|
|
|
// Brand new track
|
|
|
|
//console.log(this.icao + " new track");
|
2016-07-02 15:44:14 +02:00
|
|
|
var newseg = { fixed: new ol.geom.LineString([projHere]),
|
|
|
|
feature: null,
|
|
|
|
head_update: this.last_position_time,
|
|
|
|
tail_update: this.last_position_time,
|
|
|
|
estimated: false,
|
2017-01-25 23:33:28 +01:00
|
|
|
ground: (this.altitude === "ground"),
|
|
|
|
altitude: this.altitude
|
2015-01-07 18:18:33 +01:00
|
|
|
};
|
|
|
|
this.track_linesegs.push(newseg);
|
2016-07-02 15:44:14 +02:00
|
|
|
this.history_size ++;
|
2015-01-07 18:18:33 +01:00
|
|
|
return;
|
|
|
|
}
|
2016-07-02 15:44:14 +02:00
|
|
|
|
2015-01-07 18:18:33 +01:00
|
|
|
var lastseg = this.track_linesegs[this.track_linesegs.length - 1];
|
|
|
|
var elapsed = (this.last_position_time - lastseg.head_update);
|
|
|
|
|
2015-01-15 22:01:14 +01:00
|
|
|
var est_track = (elapsed > estimate_time);
|
2015-01-07 18:18:33 +01:00
|
|
|
var ground_track = (this.altitude === "ground");
|
|
|
|
|
|
|
|
if (est_track) {
|
2016-07-02 15:44:14 +02:00
|
|
|
|
2015-01-07 18:18:33 +01:00
|
|
|
if (!lastseg.estimated) {
|
|
|
|
// >5s gap in data, create a new estimated segment
|
|
|
|
//console.log(this.icao + " switching to estimated");
|
2016-07-02 15:44:14 +02:00
|
|
|
lastseg.fixed.appendCoordinate(projPrev);
|
|
|
|
this.track_linesegs.push({ fixed: new ol.geom.LineString([projPrev, projHere]),
|
|
|
|
feature: null,
|
|
|
|
head_update: this.last_position_time,
|
2017-01-26 18:19:10 +01:00
|
|
|
altitude: 0,
|
2016-07-02 15:44:14 +02:00
|
|
|
estimated: true });
|
2015-01-07 17:32:58 +01:00
|
|
|
this.history_size += 2;
|
2016-07-02 15:44:14 +02:00
|
|
|
} else {
|
|
|
|
// Keep appending to the existing dashed line; keep every point
|
|
|
|
lastseg.fixed.appendCoordinate(projPrev);
|
|
|
|
lastseg.head_update = this.last_position_time;
|
|
|
|
this.history_size++;
|
2015-01-06 21:15:25 +01:00
|
|
|
}
|
2016-07-02 15:44:14 +02:00
|
|
|
|
2015-01-07 02:19:05 +01:00
|
|
|
return true;
|
2015-01-07 18:18:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (lastseg.estimated) {
|
2016-07-02 15:44:14 +02:00
|
|
|
// We are back to good data (we got two points close in time), switch back to
|
|
|
|
// solid lines.
|
|
|
|
lastseg = { fixed: new ol.geom.LineString([projPrev]),
|
|
|
|
feature: null,
|
|
|
|
head_update: this.last_position_time,
|
|
|
|
tail_update: this.last_position_time,
|
|
|
|
estimated: false,
|
2017-01-26 18:19:10 +01:00
|
|
|
ground: (this.altitude === "ground"),
|
|
|
|
altitude: this.altitude };
|
2016-07-02 15:44:14 +02:00
|
|
|
this.track_linesegs.push(lastseg);
|
|
|
|
this.history_size ++;
|
|
|
|
// continue
|
2015-01-07 18:18:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( (lastseg.ground && this.altitude !== "ground") ||
|
2017-01-26 18:19:10 +01:00
|
|
|
(!lastseg.ground && this.altitude === "ground") || this.altitude !== lastseg.altitude ) {
|
2015-01-07 18:18:33 +01:00
|
|
|
//console.log(this.icao + " ground state changed");
|
|
|
|
// Create a new segment as the ground state changed.
|
|
|
|
// assume the state changed halfway between the two points
|
2016-07-02 15:44:14 +02:00
|
|
|
// FIXME needs reimplementing post-google
|
|
|
|
|
|
|
|
lastseg.fixed.appendCoordinate(projPrev);
|
|
|
|
this.track_linesegs.push({ fixed: new ol.geom.LineString([projPrev, projHere]),
|
|
|
|
feature: null,
|
|
|
|
head_update: this.last_position_time,
|
|
|
|
tail_update: this.last_position_time,
|
|
|
|
estimated: false,
|
2017-01-25 23:33:28 +01:00
|
|
|
altitude: this.altitude,
|
2016-07-02 15:44:14 +02:00
|
|
|
ground: (this.altitude === "ground") });
|
|
|
|
this.history_size += 3;
|
2015-01-07 18:18:33 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add more data to the existing track.
|
|
|
|
// We only retain some historical points, at 5+ second intervals,
|
|
|
|
// plus the most recent point
|
|
|
|
if (this.last_position_time - lastseg.tail_update >= 5) {
|
|
|
|
// enough time has elapsed; retain the last point and add a new one
|
|
|
|
//console.log(this.icao + " retain last point");
|
2016-07-02 15:44:14 +02:00
|
|
|
lastseg.fixed.appendCoordinate(projHere);
|
2015-01-07 18:18:33 +01:00
|
|
|
lastseg.tail_update = lastseg.head_update;
|
|
|
|
this.history_size ++;
|
|
|
|
}
|
2016-07-02 15:44:14 +02:00
|
|
|
|
2015-01-07 18:18:33 +01:00
|
|
|
lastseg.head_update = this.last_position_time;
|
|
|
|
return true;
|
|
|
|
};
|
2013-05-24 04:15:37 +02:00
|
|
|
|
2015-01-07 18:18:33 +01:00
|
|
|
// This is to remove the line from the screen if we deselect the plane
|
|
|
|
PlaneObject.prototype.clearLines = function() {
|
2016-07-02 15:44:14 +02:00
|
|
|
for (var i = this.track_linesegs.length - 1; i >= 0 ; --i) {
|
2015-01-07 18:18:33 +01:00
|
|
|
var seg = this.track_linesegs[i];
|
2016-07-02 15:44:14 +02:00
|
|
|
if (seg.feature !== null) {
|
|
|
|
PlaneTrailFeatures.remove(seg.feature);
|
|
|
|
seg.feature = null;
|
2015-01-06 00:20:03 +01:00
|
|
|
}
|
2015-01-07 18:18:33 +01:00
|
|
|
}
|
2016-09-15 13:37:30 +02:00
|
|
|
|
|
|
|
if (this.elastic_feature !== null) {
|
|
|
|
PlaneTrailFeatures.remove(this.elastic_feature);
|
|
|
|
this.elastic_feature = null;
|
|
|
|
}
|
2015-01-07 18:18:33 +01:00
|
|
|
};
|
2015-01-07 02:19:05 +01:00
|
|
|
|
2016-08-20 00:37:43 +02:00
|
|
|
PlaneObject.prototype.getDataSource = function() {
|
|
|
|
// MLAT
|
|
|
|
if (this.position_from_mlat) {
|
|
|
|
return 'mlat';
|
|
|
|
}
|
|
|
|
|
2016-10-14 22:52:04 +02:00
|
|
|
// Not MLAT, but position reported - ADSB or variants
|
2016-08-20 00:37:43 +02:00
|
|
|
if (this.position !== null) {
|
2016-10-14 22:52:04 +02:00
|
|
|
return this.addrtype;
|
2016-08-20 00:37:43 +02:00
|
|
|
}
|
|
|
|
|
2016-09-14 20:10:20 +02:00
|
|
|
// Otherwise Mode S
|
2016-08-20 00:37:43 +02:00
|
|
|
return 'mode_s';
|
2016-09-14 20:10:20 +02:00
|
|
|
|
|
|
|
// TODO: add support for Mode A/C
|
2016-08-20 00:37:43 +02:00
|
|
|
};
|
|
|
|
|
2015-02-21 19:41:59 +01:00
|
|
|
PlaneObject.prototype.getMarkerColor = function() {
|
|
|
|
// Emergency squawks override everything else
|
2015-01-07 18:18:33 +01:00
|
|
|
if (this.squawk in SpecialSquawks)
|
2015-02-21 19:41:59 +01:00
|
|
|
return SpecialSquawks[this.squawk].markerColor;
|
|
|
|
|
|
|
|
var h, s, l;
|
|
|
|
|
2017-01-25 23:33:28 +01:00
|
|
|
var colorArr = this.getAltitudeColor();
|
2015-02-21 19:41:59 +01:00
|
|
|
|
2017-01-25 23:33:28 +01:00
|
|
|
h = colorArr[0];
|
|
|
|
s = colorArr[1];
|
|
|
|
l = colorArr[2];
|
2015-02-21 19:41:59 +01:00
|
|
|
|
|
|
|
// If we have not seen a recent position update, change color
|
|
|
|
if (this.seen_pos > 15) {
|
|
|
|
h += ColorByAlt.stale.h;
|
|
|
|
s += ColorByAlt.stale.s;
|
|
|
|
l += ColorByAlt.stale.l;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If this marker is selected, change color
|
2016-09-22 16:28:51 +02:00
|
|
|
if (this.selected && !SelectedAllPlanes){
|
2015-02-21 19:41:59 +01:00
|
|
|
h += ColorByAlt.selected.h;
|
|
|
|
s += ColorByAlt.selected.s;
|
|
|
|
l += ColorByAlt.selected.l;
|
|
|
|
}
|
|
|
|
|
2015-10-26 13:30:21 +01:00
|
|
|
// If this marker is a mlat position, change color
|
|
|
|
if (this.position_from_mlat) {
|
|
|
|
h += ColorByAlt.mlat.h;
|
|
|
|
s += ColorByAlt.mlat.s;
|
|
|
|
l += ColorByAlt.mlat.l;
|
|
|
|
}
|
|
|
|
|
2015-02-21 19:41:59 +01:00
|
|
|
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;
|
|
|
|
|
2016-07-02 18:11:08 +02:00
|
|
|
return 'hsl(' + (h/5).toFixed(0)*5 + ',' + (s/5).toFixed(0)*5 + '%,' + (l/5).toFixed(0)*5 + '%)'
|
2015-02-21 19:41:59 +01:00
|
|
|
}
|
|
|
|
|
2017-01-25 23:33:28 +01:00
|
|
|
PlaneObject.prototype.getAltitudeColor = function(altitude) {
|
|
|
|
var h, s, l;
|
|
|
|
|
|
|
|
if (typeof altitude === 'undefined') {
|
|
|
|
altitude = this.altitude;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (altitude === null) {
|
|
|
|
h = ColorByAlt.unknown.h;
|
|
|
|
s = ColorByAlt.unknown.s;
|
|
|
|
l = ColorByAlt.unknown.l;
|
|
|
|
} else if (this.altitude === "ground") {
|
|
|
|
h = ColorByAlt.ground.h;
|
|
|
|
s = ColorByAlt.ground.s;
|
|
|
|
l = ColorByAlt.ground.l;
|
|
|
|
} else {
|
|
|
|
s = ColorByAlt.air.s;
|
|
|
|
l = ColorByAlt.air.l;
|
|
|
|
|
|
|
|
// find the pair of points the current altitude lies between,
|
|
|
|
// and interpolate the hue between those points
|
|
|
|
var hpoints = ColorByAlt.air.h;
|
|
|
|
h = hpoints[0].val;
|
|
|
|
for (var i = hpoints.length-1; i >= 0; --i) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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];
|
|
|
|
}
|
|
|
|
|
2015-02-21 19:41:59 +01:00
|
|
|
PlaneObject.prototype.updateIcon = function() {
|
2016-10-14 22:44:21 +02:00
|
|
|
var scaleFactor = Math.max(0.2, Math.min(1.2, 0.15 * Math.pow(1.25, ZoomLvl))).toFixed(1);
|
2016-09-16 12:03:54 +02:00
|
|
|
|
2015-02-21 19:41:59 +01:00
|
|
|
var col = this.getMarkerColor();
|
2016-09-16 12:03:54 +02:00
|
|
|
var opacity = 1.0;
|
2015-10-26 13:30:21 +01:00
|
|
|
var outline = (this.position_from_mlat ? OutlineMlatColor : OutlineADSBColor);
|
2017-03-22 15:27:39 +01:00
|
|
|
var add_stroke = (this.selected && !SelectedAllPlanes) ? ' stroke="black" stroke-width="1px"' : '';
|
2016-08-30 00:12:52 +02:00
|
|
|
var baseMarker = getBaseMarker(this.category, this.icaotype, this.typeDescription, this.wtc);
|
2019-03-04 16:13:38 +01:00
|
|
|
var rotation = this.track;
|
|
|
|
if (rotation === null) {
|
|
|
|
rotation = this.true_heading;
|
|
|
|
}
|
|
|
|
if (rotation === null) {
|
|
|
|
rotation = this.mag_heading;
|
|
|
|
}
|
|
|
|
if (rotation === null) {
|
|
|
|
rotation = 0;
|
|
|
|
}
|
2017-01-24 23:54:06 +01:00
|
|
|
//var transparentBorderWidth = (32 / baseMarker.scale / scaleFactor).toFixed(1);
|
2016-07-02 18:10:52 +02:00
|
|
|
|
2017-03-21 16:00:07 +01:00
|
|
|
var svgKey = col + '!' + outline + '!' + baseMarker.svg + '!' + add_stroke + "!" + scaleFactor;
|
2016-07-02 18:10:52 +02:00
|
|
|
var styleKey = opacity + '!' + rotation;
|
|
|
|
|
|
|
|
if (this.markerStyle === null || this.markerIcon === null || this.markerSvgKey != svgKey) {
|
2016-07-02 19:10:15 +02:00
|
|
|
//console.log(this.icao + " new icon and style " + this.markerSvgKey + " -> " + svgKey);
|
2016-07-02 18:10:52 +02:00
|
|
|
|
2016-08-29 13:38:00 +02:00
|
|
|
var icon = new ol.style.Icon({
|
2017-01-24 23:54:06 +01:00
|
|
|
anchor: [0.5, 0.5],
|
|
|
|
anchorXUnits: 'fraction',
|
|
|
|
anchorYUnits: 'fraction',
|
|
|
|
scale: 1.2 * scaleFactor,
|
2016-08-27 16:49:04 +02:00
|
|
|
imgSize: baseMarker.size,
|
2017-03-21 16:00:07 +01:00
|
|
|
src: svgPathToURI(baseMarker.svg, outline, col, add_stroke),
|
2016-08-29 13:38:00 +02:00
|
|
|
rotation: (baseMarker.noRotate ? 0 : rotation * Math.PI / 180.0),
|
|
|
|
opacity: opacity,
|
|
|
|
rotateWithView: (baseMarker.noRotate ? false : true)
|
2016-07-02 18:10:52 +02:00
|
|
|
});
|
|
|
|
|
2017-02-10 19:18:13 +01:00
|
|
|
this.markerIcon = icon;
|
|
|
|
this.markerStyle = new ol.style.Style({
|
|
|
|
image: this.markerIcon
|
|
|
|
});
|
|
|
|
this.markerStaticIcon = null;
|
|
|
|
this.markerStaticStyle = new ol.style.Style({});
|
2016-08-29 13:38:00 +02:00
|
|
|
|
2016-07-02 18:10:52 +02:00
|
|
|
this.markerStyleKey = styleKey;
|
|
|
|
this.markerSvgKey = svgKey;
|
|
|
|
|
|
|
|
if (this.marker !== null) {
|
|
|
|
this.marker.setStyle(this.markerStyle);
|
2016-08-29 13:38:00 +02:00
|
|
|
this.markerStatic.setStyle(this.markerStaticStyle);
|
2016-07-02 18:10:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.markerStyleKey != styleKey) {
|
2016-07-02 19:10:15 +02:00
|
|
|
//console.log(this.icao + " new rotation");
|
2016-07-02 18:10:52 +02:00
|
|
|
this.markerIcon.setRotation(rotation * Math.PI / 180.0);
|
|
|
|
this.markerIcon.setOpacity(opacity);
|
2016-08-29 13:38:00 +02:00
|
|
|
if (this.staticIcon) {
|
|
|
|
this.staticIcon.setOpacity(opacity);
|
|
|
|
}
|
2016-07-02 18:10:52 +02:00
|
|
|
this.markerStyleKey = styleKey;
|
2016-07-02 15:44:14 +02:00
|
|
|
}
|
|
|
|
|
2015-01-07 18:18:33 +01:00
|
|
|
return true;
|
|
|
|
};
|
2013-05-24 04:15:37 +02:00
|
|
|
|
2015-01-07 18:18:33 +01:00
|
|
|
// Update our data
|
|
|
|
PlaneObject.prototype.updateData = function(receiver_timestamp, data) {
|
|
|
|
// Update all of our data
|
|
|
|
this.messages = data.messages;
|
2015-01-22 16:31:10 +01:00
|
|
|
this.rssi = data.rssi;
|
2015-01-07 18:18:33 +01:00
|
|
|
this.last_message_time = receiver_timestamp - data.seen;
|
2017-06-16 11:39:57 +02:00
|
|
|
|
|
|
|
// simple fields
|
|
|
|
|
2018-03-08 18:34:34 +01:00
|
|
|
var fields = ["alt_baro", "alt_geom", "gs", "ias", "tas", "track",
|
2017-06-16 11:39:57 +02:00
|
|
|
"track_rate", "mag_heading", "true_heading", "mach",
|
2019-03-19 19:48:13 +01:00
|
|
|
"roll", "nav_heading", "nav_modes",
|
2018-07-31 19:17:27 +02:00
|
|
|
"nac_p", "nac_v", "nic_baro", "sil_type", "sil",
|
2018-08-02 20:04:44 +02:00
|
|
|
"nav_qnh", "baro_rate", "geom_rate", "rc",
|
2017-06-16 11:39:57 +02:00
|
|
|
"squawk", "category", "version"];
|
|
|
|
|
|
|
|
for (var i = 0; i < fields.length; ++i) {
|
|
|
|
if (fields[i] in data) {
|
|
|
|
this[fields[i]] = data[fields[i]];
|
|
|
|
} else {
|
|
|
|
this[fields[i]] = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// fields with more complex behaviour
|
2015-01-07 18:18:33 +01:00
|
|
|
|
2017-06-16 11:39:57 +02:00
|
|
|
if ('type' in data)
|
2016-10-14 22:52:04 +02:00
|
|
|
this.addrtype = data.type;
|
|
|
|
else
|
|
|
|
this.addrtype = 'adsb_icao';
|
|
|
|
|
2017-06-16 14:30:30 +02:00
|
|
|
// don't expire callsigns
|
|
|
|
if ('flight' in data)
|
|
|
|
this.flight = data.flight;
|
|
|
|
|
2017-06-16 11:39:57 +02:00
|
|
|
if ('lat' in data && 'lon' in data) {
|
2016-07-02 15:44:14 +02:00
|
|
|
this.position = [data.lon, data.lat];
|
2015-01-07 18:18:33 +01:00
|
|
|
this.last_position_time = receiver_timestamp - data.seen_pos;
|
2013-05-24 04:15:37 +02:00
|
|
|
|
2015-01-07 18:18:33 +01:00
|
|
|
if (SitePosition !== null) {
|
2016-07-02 15:44:14 +02:00
|
|
|
var WGS84 = new ol.Sphere(6378137);
|
|
|
|
this.sitedist = WGS84.haversineDistance(SitePosition, this.position);
|
2015-01-07 18:18:33 +01:00
|
|
|
}
|
2015-06-29 16:51:42 +02:00
|
|
|
|
|
|
|
this.position_from_mlat = false;
|
|
|
|
if (typeof data.mlat !== "undefined") {
|
|
|
|
for (var i = 0; i < data.mlat.length; ++i) {
|
|
|
|
if (data.mlat[i] === "lat" || data.mlat[i] == "lon") {
|
|
|
|
this.position_from_mlat = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-01-07 18:18:33 +01:00
|
|
|
}
|
2018-03-08 18:34:34 +01:00
|
|
|
|
|
|
|
// Pick an altitude
|
|
|
|
if ('alt_baro' in data) {
|
|
|
|
this.altitude = data.alt_baro;
|
|
|
|
} else if ('alt_geom' in data) {
|
|
|
|
this.altitude = data.alt_geom;
|
|
|
|
} else {
|
|
|
|
this.altitude = null;
|
|
|
|
}
|
|
|
|
|
2019-03-19 19:48:13 +01:00
|
|
|
// Pick a selected altitude
|
|
|
|
if ('nav_altitude_fms' in data) {
|
|
|
|
this.nav_altitude = data.nav_altitude_fms;
|
|
|
|
} else if ('nav_altitude_mcp' in data) {
|
|
|
|
this.nav_altitude = data.nav_altitude_mcp;
|
|
|
|
} else {
|
|
|
|
this.nav_altitude = null;
|
|
|
|
}
|
|
|
|
|
2018-03-08 18:34:34 +01:00
|
|
|
// Pick vertical rate from either baro or geom rate
|
|
|
|
// geometric rate is generally more reliable (smoothed etc)
|
|
|
|
if ('geom_rate' in data) {
|
|
|
|
this.vert_rate = data.geom_rate;
|
|
|
|
} else if ('baro_rate' in data) {
|
|
|
|
this.vert_rate = data.baro_rate;
|
|
|
|
} else {
|
|
|
|
this.vert_rate = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pick a speed
|
|
|
|
if ('gs' in data) {
|
|
|
|
this.speed = data.gs;
|
|
|
|
} else if ('tas' in data) {
|
|
|
|
this.speed = data.tas;
|
|
|
|
} else if ('ias' in data) {
|
|
|
|
this.speed = data.ias;
|
|
|
|
} else {
|
|
|
|
this.speed = null;
|
|
|
|
}
|
2015-01-07 18:18:33 +01:00
|
|
|
};
|
2013-05-24 04:15:37 +02:00
|
|
|
|
2015-02-11 01:15:48 +01:00
|
|
|
PlaneObject.prototype.updateTick = function(receiver_timestamp, last_timestamp) {
|
2015-01-07 18:18:33 +01:00
|
|
|
// recompute seen and seen_pos
|
|
|
|
this.seen = receiver_timestamp - this.last_message_time;
|
|
|
|
this.seen_pos = (this.last_position_time === null ? null : receiver_timestamp - this.last_position_time);
|
|
|
|
|
|
|
|
// If no packet in over 58 seconds, clear the plane.
|
|
|
|
if (this.seen > 58) {
|
|
|
|
if (this.visible) {
|
|
|
|
//console.log("hiding " + this.icao);
|
2015-01-07 02:19:05 +01:00
|
|
|
this.clearMarker();
|
2015-01-07 18:18:33 +01:00
|
|
|
this.visible = false;
|
|
|
|
if (SelectedPlane == this.icao)
|
2015-01-22 22:35:59 +01:00
|
|
|
selectPlaneByHex(null,false);
|
2015-01-07 18:18:33 +01:00
|
|
|
}
|
|
|
|
} else {
|
2015-07-15 18:07:28 +02:00
|
|
|
if (this.position !== null && (this.selected || this.seen_pos < 60)) {
|
2016-09-16 12:04:33 +02:00
|
|
|
this.visible = true;
|
2015-06-29 13:44:19 +02:00
|
|
|
if (this.updateTrack(receiver_timestamp - last_timestamp + (this.position_from_mlat ? 30 : 5))) {
|
2015-01-07 18:18:33 +01:00
|
|
|
this.updateLines();
|
|
|
|
this.updateMarker(true);
|
|
|
|
} else {
|
|
|
|
this.updateMarker(false); // didn't move
|
|
|
|
}
|
2015-07-15 18:07:28 +02:00
|
|
|
} else {
|
|
|
|
this.clearMarker();
|
2016-09-16 12:04:33 +02:00
|
|
|
this.visible = false;
|
2015-07-15 18:07:28 +02:00
|
|
|
}
|
2015-01-07 18:18:33 +01:00
|
|
|
}
|
|
|
|
};
|
2013-05-24 04:15:37 +02:00
|
|
|
|
2015-01-07 18:18:33 +01:00
|
|
|
PlaneObject.prototype.clearMarker = function() {
|
|
|
|
if (this.marker) {
|
2016-07-02 15:44:14 +02:00
|
|
|
PlaneIconFeatures.remove(this.marker);
|
2016-08-29 13:38:00 +02:00
|
|
|
PlaneIconFeatures.remove(this.markerStatic);
|
2016-07-02 15:44:14 +02:00
|
|
|
/* FIXME google.maps.event.clearListeners(this.marker, 'click'); */
|
2016-08-29 13:38:00 +02:00
|
|
|
this.marker = this.markerStatic = null;
|
2015-01-07 18:18:33 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Update our marker on the map
|
|
|
|
PlaneObject.prototype.updateMarker = function(moved) {
|
2016-08-24 22:28:13 +02:00
|
|
|
if (!this.visible || this.position == null || this.isFiltered()) {
|
2015-01-07 18:18:33 +01:00
|
|
|
this.clearMarker();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-07-02 18:10:52 +02:00
|
|
|
this.updateIcon();
|
2016-07-02 15:44:14 +02:00
|
|
|
if (this.marker) {
|
|
|
|
if (moved) {
|
|
|
|
this.marker.setGeometry(new ol.geom.Point(ol.proj.fromLonLat(this.position)));
|
2016-08-29 13:38:00 +02:00
|
|
|
this.markerStatic.setGeometry(new ol.geom.Point(ol.proj.fromLonLat(this.position)));
|
2016-07-02 15:44:14 +02:00
|
|
|
}
|
2016-08-29 13:38:00 +02:00
|
|
|
} else {
|
|
|
|
this.marker = new ol.Feature(new ol.geom.Point(ol.proj.fromLonLat(this.position)));
|
2016-07-02 18:11:33 +02:00
|
|
|
this.marker.hex = this.icao;
|
2016-07-02 18:10:52 +02:00
|
|
|
this.marker.setStyle(this.markerStyle);
|
2016-07-02 15:44:14 +02:00
|
|
|
PlaneIconFeatures.push(this.marker);
|
2016-08-29 13:38:00 +02:00
|
|
|
|
|
|
|
this.markerStatic = new ol.Feature(new ol.geom.Point(ol.proj.fromLonLat(this.position)));
|
|
|
|
this.markerStatic.hex = this.icao;
|
|
|
|
this.markerStatic.setStyle(this.markerStaticStyle);
|
|
|
|
PlaneIconFeatures.push(this.markerStatic);
|
2015-01-07 18:18:33 +01:00
|
|
|
}
|
|
|
|
};
|
2015-01-06 21:15:25 +01:00
|
|
|
|
2017-01-25 23:33:28 +01:00
|
|
|
|
|
|
|
// return the styling of the lines based on altitude
|
|
|
|
PlaneObject.prototype.altitudeLines = function(altitude) {
|
|
|
|
var colorArr = this.getAltitudeColor(altitude);
|
|
|
|
return new ol.style.Style({
|
|
|
|
stroke: new ol.style.Stroke({
|
|
|
|
color: 'hsl(' + (colorArr[0]/5).toFixed(0)*5 + ',' + (colorArr[1]/5).toFixed(0)*5 + '%,' + (colorArr[2]/5).toFixed(0)*5 + '%)',
|
|
|
|
width: 2
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2015-01-07 18:18:33 +01:00
|
|
|
// Update our planes tail line,
|
|
|
|
PlaneObject.prototype.updateLines = function() {
|
2015-01-07 18:32:20 +01:00
|
|
|
if (!this.selected)
|
2015-01-07 18:18:33 +01:00
|
|
|
return;
|
2016-07-24 17:58:13 +02:00
|
|
|
|
|
|
|
if (this.track_linesegs.length == 0)
|
|
|
|
return;
|
|
|
|
|
2016-07-02 15:44:14 +02:00
|
|
|
var estimateStyle = new ol.style.Style({
|
|
|
|
stroke: new ol.style.Stroke({
|
|
|
|
color: '#a08080',
|
|
|
|
width: 1.5,
|
|
|
|
lineDash: [3, 3]
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
var airStyle = new ol.style.Style({
|
|
|
|
stroke: new ol.style.Stroke({
|
|
|
|
color: '#000000',
|
|
|
|
width: 2
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
var groundStyle = new ol.style.Style({
|
|
|
|
stroke: new ol.style.Stroke({
|
|
|
|
color: '#408040',
|
|
|
|
width: 2
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
2016-09-15 13:37:30 +02:00
|
|
|
// find the old elastic band so we can replace it in place
|
|
|
|
// (which should be faster than remove-and-add when PlaneTrailFeatures is large)
|
|
|
|
var oldElastic = -1;
|
|
|
|
if (this.elastic_feature !== null) {
|
|
|
|
oldElastic = PlaneTrailFeatures.getArray().indexOf(this.elastic_feature);
|
|
|
|
}
|
|
|
|
|
|
|
|
// create the new elastic band feature
|
2016-07-02 15:44:14 +02:00
|
|
|
var lastseg = this.track_linesegs[this.track_linesegs.length - 1];
|
|
|
|
var lastfixed = lastseg.fixed.getCoordinateAt(1.0);
|
|
|
|
var geom = new ol.geom.LineString([lastfixed, ol.proj.fromLonLat(this.position)]);
|
2016-09-15 13:37:30 +02:00
|
|
|
this.elastic_feature = new ol.Feature(geom);
|
2017-01-26 18:19:10 +01:00
|
|
|
this.elastic_feature.setStyle(this.altitudeLines(lastseg.altitude));
|
2016-07-02 15:44:14 +02:00
|
|
|
|
2016-09-15 13:37:30 +02:00
|
|
|
if (oldElastic < 0) {
|
|
|
|
PlaneTrailFeatures.push(this.elastic_feature);
|
2016-07-02 15:44:14 +02:00
|
|
|
} else {
|
2016-09-15 13:37:30 +02:00
|
|
|
PlaneTrailFeatures.setAt(oldElastic, this.elastic_feature);
|
2016-07-02 15:44:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// create any missing fixed line features
|
2015-01-07 18:18:33 +01:00
|
|
|
for (var i = 0; i < this.track_linesegs.length; ++i) {
|
|
|
|
var seg = this.track_linesegs[i];
|
2016-07-02 15:44:14 +02:00
|
|
|
if (seg.feature === null) {
|
|
|
|
seg.feature = new ol.Feature(seg.fixed);
|
2015-01-07 18:18:33 +01:00
|
|
|
if (seg.estimated) {
|
2016-07-02 15:44:14 +02:00
|
|
|
seg.feature.setStyle(estimateStyle);
|
2015-01-07 18:18:33 +01:00
|
|
|
} else {
|
2017-01-25 23:33:28 +01:00
|
|
|
seg.feature.setStyle(this.altitudeLines(seg.altitude));
|
2015-01-06 02:00:44 +01:00
|
|
|
}
|
2016-07-02 15:44:14 +02:00
|
|
|
|
|
|
|
PlaneTrailFeatures.push(seg.feature);
|
2015-01-06 02:00:44 +01:00
|
|
|
}
|
|
|
|
}
|
2013-05-24 04:15:37 +02:00
|
|
|
};
|
2015-01-07 18:18:33 +01:00
|
|
|
|
|
|
|
PlaneObject.prototype.destroy = function() {
|
|
|
|
this.clearLines();
|
|
|
|
this.clearMarker();
|
|
|
|
};
|