项目更新

This commit is contained in:
2026-08-27 16:57:16 +08:00
parent 452f651ca2
commit c8e146e5ac
6 changed files with 82 additions and 95 deletions
@@ -78,7 +78,6 @@ class Data_Source_Aircraft_Stream {
source.apply_aircraft_delta(Array.isArray(source_update.change_list) ? source_update.change_list : [], Array.isArray(source_update.removed_icaos) ? source_update.removed_icaos : []);
source.apply_track_stream_list(Array.isArray(source_update.tracks) ? source_update.tracks : []);
}
app.cesium_map?.request_sync_data_sources();
}
}
@@ -164,6 +164,7 @@ export class Data_Source_Map_Runtime {
aircraft.refresh_from_change_list(item);
this.aircraft_map.set(item.icao, aircraft);
this.aircraft_change_versions.set(item.icao, String(item.change_version || ""));
app.cesium_map?.update_aircraft(aircraft);
}
this.refresh_aircraft_range();
}
@@ -173,6 +174,7 @@ export class Data_Source_Map_Runtime {
if (!aircraft) return;
aircraft.hide_track();
aircraft.pos_marker.remove();
app.cesium_map?.remove_aircraft(app.cesium_map.entity_key(this.source.key, icao));
this.aircraft_map.delete(icao);
this.aircraft_change_versions.delete(icao);
if (this.active_aircraft === aircraft) this.active_aircraft = null;
@@ -216,6 +218,7 @@ export class Data_Source_Map_Runtime {
if (!aircraft) continue;
aircraft.is_show_path = true;
aircraft.append_path_response(item);
app.cesium_map?.update_aircraft(aircraft);
}
}
}
+11 -38
View File
@@ -4,7 +4,7 @@ import {app} from "../App.tsx"
import {baseURL, lightenColor, StateHolder} from "../Global.ts";
import axios from "axios";
import {Data_Source} from "../Data_Source/Data_Source.tsx";
import {Aircraft_Model, Aircraft_Status, get_aircraft_angle, orientation_from_item} from "./Aircraft_Model.tsx";
import {Aircraft_Model, Aircraft_Status} from "./Aircraft_Model.tsx";
class Aircraft_Marker extends Marker {
constructor(latlng: LatLngExpression, options?: MarkerOptions) {
@@ -74,7 +74,6 @@ export class Aircraft {
mlat: boolean
track_point_list: Aircraft_Track_Point[] = []
data_list: any[] = [];
data_model: Aircraft_Model;
status = new StateHolder<Aircraft_Status>(Aircraft_Status.Need_Init);
angle = new StateHolder<number>(0);
@@ -113,17 +112,16 @@ export class Aircraft {
const svg = this.generateSVG(color, size, rotationDegree);
const selected = this.data_source.active_aircraft === this;
const outline = selected ? "filter: drop-shadow(1px 0 0 #111) drop-shadow(-1px 0 0 #111) drop-shadow(0 1px 0 #111) drop-shadow(0 -1px 0 #111) drop-shadow(0 0 4px #fff);" : "";
const positionStyle = this.get_label_position(size);
const lines: string[] = [];
if (style.show_icao) lines.push(this.icao);
if (style.show_call_sign) lines.push(this.call_sign);
if (style.show_fly_status) lines.push(this.fly_status);
if (style.show_heading) {
lines.push(`航向 H ${this.data_model.heading.toFixed(1)}°`);
lines.push(`vortex_type ${this.data_model.vortexType ?? "未知"}`);
}
let label=`</>`
if (lines.length != 0) {
label = `
<div style="position: absolute; ${positionStyle} font-size: 12px; transform: rotate(0deg); background-color: rgba(255, 255, 255); padding: 0; margin: 0; border-radius: 5px;">
@@ -131,8 +129,6 @@ export class Aircraft {
</div>
`;
}
return L.divIcon({
html: `
<div style="position: relative; width: ${size}px; height: ${size}px;">
@@ -193,27 +189,13 @@ export class Aircraft {
});
this.refresh();
}
refresh_pos_by_track() {
let list = this.data_list;
if (list.length >= 1) {
let last_data = list[list.length - 1];
let last_pos = L.latLng(last_data.lat, last_data.lon);
if (list.length >= 2) {
let second_last_data = list[list.length - 2];
let second_last_pos = L.latLng(second_last_data.lat, second_last_data.lon, second_last_data.alt);
let angle = get_aircraft_angle(second_last_pos, last_pos);
this.angle.set(angle);
// @ts-ignore
this.pos_marker.setRotationAngle(this.angle);
const points = this.data_model.track_points;
if (points.length === 0) return;
const last = points[points.length - 1];
this.set_angle(this.data_model.heading)
this.pos_marker.setLatLng(L.latLng(last.lat, last.lon, last.alt))
}
this.pos_marker.setLatLng(last_pos);
}
}
refresh_from_change_list(item) {
const previous_alt = this.data_model.status === Aircraft_Status.Need_Init ? undefined : this.data_model.alt
this.data_model.update_from_change(item, previous_alt)
@@ -228,13 +210,8 @@ export class Aircraft {
set_pos(lat: number, lon: number, alt : number) {
let pos = L.latLng(lat, lon, alt);
this.pos = pos;
let old_pos = this.pos_marker.getLatLng();
if (pos.lat != old_pos.lat || pos.lng != old_pos.lng) {
let angle = get_aircraft_angle(old_pos, pos);
this.set_angle(angle)
this.pos_marker.setLatLng(pos);
}
}
apply_track_data(data: any) {
if (!data) {
@@ -249,13 +226,9 @@ export class Aircraft {
}
this.last_size = end_seq;
if (data.track_orientation) {
this.data_model.trackOrientation = orientation_from_item(data, this.data_model.trackOrientation);
this.data_model.heading = this.data_model.trackOrientation.heading;
this.data_model.pitch = this.data_model.trackOrientation.pitch;
this.data_model.roll = this.data_model.trackOrientation.roll;
this.data_model.update_orientation(data)
}
this.data_model.append_track_points(list)
this.data_list.push(...list);
return list
}
+32 -6
View File
@@ -46,10 +46,7 @@ export class Aircraft_Model {
this.lat = Number(item.latitude)
this.lon = Number(item.longitude)
this.alt = Number(item.altitude || 0)
this.trackOrientation = orientation_from_item(item, this.trackOrientation)
this.heading = this.trackOrientation.heading
this.pitch = this.trackOrientation.pitch
this.roll = this.trackOrientation.roll
this.update_orientation(item)
this.timestamp = item.utc ?? item.timestamp ?? item.last_seen ?? this.timestamp
this.call_sign = item.call_sign ?? this.call_sign
this.vortexType = typeof item.vortex_type === "number" ? item.vortex_type : this.vortexType
@@ -75,6 +72,24 @@ export class Aircraft_Model {
properties: rest
})
}
this.resolve_orientation()
}
update_orientation(item: any) {
this.trackOrientation = orientation_from_item(item, this.trackOrientation)
this.resolve_orientation()
}
private resolve_orientation() {
if (this.track_points.length < 2) {
this.heading = this.trackOrientation.heading
this.pitch = this.trackOrientation.pitch
this.roll = this.trackOrientation.roll
return
}
const last = this.track_points.length - 1
const orientation = orientation_from_points(this.track_points[last - 1], this.track_points[last])
this.heading = orientation.heading
this.pitch = orientation.pitch
this.roll = orientation.roll
}
}
export function orientation_from_item(item: any, fallback: Aircraft_Orientation_Model): Aircraft_Orientation_Model {
@@ -110,6 +125,17 @@ function aircraft_status_text(status: Aircraft_Status): string {
[Aircraft_Status.Need_Init]: "未初始化",
}[status]
}
export function get_aircraft_angle(p1: { lat: number, lng: number }, p2: { lat: number, lng: number }) {
return Math.atan2(p2.lng - p1.lng, p2.lat - p1.lat) * 180 / Math.PI
function orientation_from_points(first: Aircraft_Track_Point_Model, second: Aircraft_Track_Point_Model): Aircraft_Orientation_Model {
const radians = Math.PI / 180
const lat1 = first.lat * radians
const lat2 = second.lat * radians
const delta_lat = (second.lat - first.lat) * radians
const delta_lon = (second.lon - first.lon) * radians
const y = Math.sin(delta_lon) * Math.cos(lat2)
const x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(delta_lon)
const heading = (Math.atan2(y, x) / radians + 360) % 360
const haversine = Math.sin(delta_lat / 2) ** 2 + Math.cos(lat1) * Math.cos(lat2) * Math.sin(delta_lon / 2) ** 2
const distance = 6371000 * 2 * Math.atan2(Math.sqrt(haversine), Math.sqrt(1 - haversine))
const pitch = Math.atan2(second.alt - first.alt, distance) / radians
return {heading, pitch, roll: 0}
}
+1 -40
View File
@@ -1,27 +1,14 @@
import type {Aircraft} from "./Aircraft.tsx";
import type {Cesium_Map} from "./Cesium_Map.tsx";
type Aircraft_Sync_Task = {
key: string
aircraft: Aircraft
}
export class Cesium_Data_Source_Sync {
private request_timer: number | null = null;
private queue_timer: number | null = null;
private generation = 0;
private aircraft_tasks: Aircraft_Sync_Task[] = [];
private refreshing = false;
constructor(private readonly map: Cesium_Map) {}
destroy() {
if (this.request_timer) window.clearTimeout(this.request_timer);
if (this.queue_timer) window.clearTimeout(this.queue_timer);
this.request_timer = null;
this.queue_timer = null;
this.aircraft_tasks = [];
this.generation++;
}
async refresh() {
@@ -47,14 +34,8 @@ export class Cesium_Data_Source_Sync {
sync() {
if (!this.map.viewer) return;
if (this.queue_timer) {
window.clearTimeout(this.queue_timer);
this.queue_timer = null;
}
const generation = ++this.generation;
const alive = new Set<string>();
const alive_base_stations = new Set<string>();
const tasks: Aircraft_Sync_Task[] = [];
for (const source of this.map.list()) {
if (!source.enable) continue;
this.map.sync_base_station(source, alive_base_stations);
@@ -62,10 +43,9 @@ export class Cesium_Data_Source_Sync {
for (const [icao, aircraft] of source.aircraftMap) {
const key = this.map.entity_key(source.key, icao);
alive.add(key);
tasks.push({key, aircraft});
this.map.sync_aircraft(key, aircraft);
}
}
this.aircraft_tasks = tasks;
for (const [key, record] of this.map.entity_map) {
if (alive.has(key)) continue;
this.map.remove_aircraft_record(record);
@@ -79,25 +59,6 @@ export class Cesium_Data_Source_Sync {
this.map.remove_base_station_record(record);
this.map.base_station_entity_map.delete(key);
}
this.process_queue(generation);
}
private process_queue(generation: number) {
if (!this.map.viewer || generation !== this.generation) return;
const start = performance.now();
let count = 0;
while (this.aircraft_tasks.length > 0 && count < 10 && (count === 0 || performance.now() - start < 8)) {
const task = this.aircraft_tasks.shift()!;
this.map.sync_aircraft(task.key, task.aircraft);
count++;
}
if (this.aircraft_tasks.length > 0) {
this.queue_timer = window.setTimeout(() => {
this.queue_timer = null;
this.process_queue(generation);
}, 180);
return;
}
this.map.viewer.scene.requestRender();
}
}
+33 -8
View File
@@ -1148,6 +1148,22 @@ export class Cesium_Map extends enhance.Base {
sync_aircraft(key: string, aircraft: Aircraft) {
this.aircraft_layer.sync(key, aircraft);
}
update_aircraft(aircraft: Aircraft) {
const key = this.entity_key(aircraft.data_source.key, aircraft.icao);
if (aircraft.data_source.enable && aircraft.data_source.map3d_style().aircraft_show) {
this.sync_aircraft(key, aircraft);
} else {
this.remove_aircraft(key);
}
this.viewer?.scene.requestRender();
}
remove_aircraft(key: string) {
const record = this.entity_map.get(key);
if (!record) return;
this.remove_aircraft_record(record);
this.entity_map.delete(key);
if (this.selected_aircraft && key === this.entity_key(this.selected_aircraft.data_source.key, this.selected_aircraft.icao)) this.clear_selected_aircraft();
}
ensure_aircraft_track_entities(key: string, aircraft: Aircraft, record: Aircraft_Entity_Record) {
this.aircraft_layer.ensure_track_entities(key, aircraft, record);
}
@@ -1238,7 +1254,6 @@ export class Cesium_Map extends enhance.Base {
record.occlusion_marker.description = new Cesium.ConstantProperty(this.aircraft_occlusion_description(aircraft, result));
}
if (record.aircraft.model) {
const selected = this.is_selected_aircraft(this.entity_key(aircraft.data_source.key, aircraft.icao));
record.aircraft.model.silhouetteColor = new Cesium.ConstantProperty(color);
record.aircraft.model.silhouetteSize = new Cesium.ConstantProperty(result ? result.obstructed ? 2 : 0 : 1);
}
@@ -1309,13 +1324,13 @@ export class Cesium_Map extends enhance.Base {
return {lon: model.lon, lat: model.lat, alt: model.alt};
}
aircraft_heading(aircraft: Aircraft): number {
return aircraft.data_model.trackOrientation.heading;
return aircraft.data_model.heading;
}
aircraft_pitch(aircraft: Aircraft): number {
return aircraft.data_model.trackOrientation.pitch || 0;
return aircraft.data_model.pitch;
}
aircraft_roll(aircraft: Aircraft): number {
return aircraft.data_model.trackOrientation.roll || 0;
return aircraft.data_model.roll;
}
aircraft_orientation(aircraft: Aircraft, position: Cesium.Cartesian3): Cesium.Quaternion {
const model = this.aircraft_model_item(aircraft);
@@ -1392,7 +1407,7 @@ export class Cesium_Map extends enhance.Base {
}
aircraft_label_enabled(aircraft: Aircraft): boolean {
const source = aircraft.data_source.map3d_style();
return source.show_icao || source.show_call_sign || source.show_fly_status;
return source.show_icao || source.show_call_sign || source.show_fly_status || source.show_heading;
}
should_show_aircraft_track(key: string, aircraft: Aircraft): boolean {
return this.is_selected_aircraft(key) || aircraft.data_source.is_aircraft_track_monitored(aircraft.icao);
@@ -1403,9 +1418,19 @@ export class Cesium_Map extends enhance.Base {
const model = aircraft.data_model;
if (source.show_icao) lines.push(model.icao);
if (source.show_call_sign && model.call_sign) lines.push(model.call_sign);
if (source.show_fly_status && model.fly_status) lines.push(`${model.fly_status} ${model.heading.toFixed(0)}°`);
if (source.show_fly_status && model.fly_status) lines.push(model.fly_status);
if (source.show_heading) {
const config = this.aircraft_model_item(aircraft);
lines.push(`vortex_type ${model.vortexType ?? "未知"}`);
lines.push(`当前 H ${model.heading.toFixed(1)}° P ${model.pitch.toFixed(1)}° R ${model.roll.toFixed(1)}°`);
lines.push(`偏移 H ${config.heading_offset_degrees.toFixed(1)}° P ${config.pitch_offset_degrees.toFixed(1)}° R ${config.roll_offset_degrees.toFixed(1)}°`);
lines.push(`模型 H ${this.normalize_degrees(model.heading + config.heading_offset_degrees).toFixed(1)}° P ${(model.pitch + config.pitch_offset_degrees).toFixed(1)}° R ${(model.roll + config.roll_offset_degrees).toFixed(1)}°`);
}
return lines.join("\n");
}
normalize_degrees(value: number): number {
return (value % 360 + 360) % 360;
}
sync_track_points(key: string, aircraft: Aircraft, record: Aircraft_Entity_Record) {
this.ensure_aircraft_track_entities(key, aircraft, record);
const points = aircraft.data_model.track_points;
@@ -1596,8 +1621,8 @@ export class Cesium_Map extends enhance.Base {
`高度: ${model.alt}`,
`航向: ${model.heading.toFixed(1)}°`,
`涡流/目标类型: ${model.vortexTypeLabel || model.vortexTypeKey || ""}`,
`航迹俯仰: ${model.trackOrientation.pitch.toFixed(1)}°`,
`航迹横滚: ${model.trackOrientation.roll.toFixed(1)}°`
`航迹俯仰: ${model.pitch.toFixed(1)}°`,
`航迹横滚: ${model.roll.toFixed(1)}°`
].join("<br>");
}
track_point_description(point: Aircraft_Track_Point_Model) {