From 28b25742cfa6496571b39f815a54c62f191c4401 Mon Sep 17 00:00:00 2001 From: wyc <1104749580@qq.com> Date: Sun, 9 Aug 2026 17:07:05 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Data_Source/Data_Source.tsx | 109 ++------- src/Data_Source/Data_Source_Config.tsx | 40 +++- src/Data_Source/Data_Source_Show.tsx | 303 ++++--------------------- src/Map/Cesium_Graphics_Config.ts | 235 ++----------------- src/Map/Cesium_Map.tsx | 132 ++++++----- src/Map/Cesium_Model_Settings.tsx | 154 ++++++------- src/Map/Map_Models.tsx | 141 ++---------- src/Map/Map_View.tsx | 101 +-------- 8 files changed, 288 insertions(+), 927 deletions(-) diff --git a/src/Data_Source/Data_Source.tsx b/src/Data_Source/Data_Source.tsx index 747f00a..d1efd79 100644 --- a/src/Data_Source/Data_Source.tsx +++ b/src/Data_Source/Data_Source.tsx @@ -7,7 +7,7 @@ import {Base_Station} from "../Map/Base_Station.tsx"; import {Aircraft, Aircraft_Track_Point} from "../Map/Aircraft.tsx"; import {app} from "../App.tsx"; import {Leaflet_Map} from "../Map/Leaflet_Map.tsx"; -import {Data_Format, Input_Number, Input_Port_Number, Input_String, Play_Mode, Psc, Switch_Bool} from "../A_Global.tsx"; +import {Data_Format, Input_Number, Input_Port_Number, Input_String, Play_Mode, Switch_Bool} from "../A_Global.tsx"; import {Data_Source_Show} from "./Data_Source_Show.tsx"; import {Camera_Control_Mode} from "../Map/Camera_Control_Mode.ts"; import L from "leaflet"; @@ -17,56 +17,8 @@ const {Text} = Typography; const {Option} = Select; export type Map_Display_Mode = "map2d" | "map3d" -export type Data_Source_Map_Display_Data = { - base_station_show: boolean - aircraft_show: boolean - constant_screen_size: boolean - color: string - track_point_color: string - base_station_color: string - aircraft_pixel_size: number - base_station_pixel_size: number - track_point_pixel_size: number - aircraft_scale: number - base_station_scale: number - show_icao: boolean - show_call_sign: boolean - show_fly_status: boolean -} -export type Data_Source_Map_Display_Config = { - map2d: Data_Source_Map_Display_Data - map3d: Data_Source_Map_Display_Data -} -export function default_map_display_data(): Data_Source_Map_Display_Data { - return { - base_station_show: true, - aircraft_show: true, - constant_screen_size: true, - color: "#3388ff", - track_point_color: "#ffff00", - base_station_color: "#1677ff", - aircraft_pixel_size: 50, - base_station_pixel_size: 100, - track_point_pixel_size: 8, - aircraft_scale: 1, - base_station_scale: 1, - show_icao: true, - show_call_sign: false, - show_fly_status: false - }; -} -function normalize_map_display_data(data: Partial | undefined): Data_Source_Map_Display_Data { - return {...default_map_display_data(), ...(data || {})}; -} -function default_map_display_config(): Data_Source_Map_Display_Config { - return {map2d: default_map_display_data(), map3d: default_map_display_data()}; -} -function normalize_map_display_config(data: Partial | undefined): Data_Source_Map_Display_Config { - return { - map2d: normalize_map_display_data(data?.map2d), - map3d: normalize_map_display_data(data?.map3d) - }; -} +export type Data_Source_Map_Display_Data = Record +export type Data_Source_Map_Display_Config = Record export class Data_Source extends enhance.Base { timer @@ -77,7 +29,7 @@ export class Data_Source extends enhance.Base { adminive_id: number = 0 enable: boolean = false; type: string = ""; // 也就是类型名 - map_display: Data_Source_Map_Display_Config = default_map_display_config(); + map_display: Data_Source_Map_Display_Config = {map2d: {}, map3d: {}}; lat: number = 0; lon: number = 0; alt: number = 0; @@ -87,13 +39,17 @@ export class Data_Source extends enhance.Base { update_form_gps: boolean = false; ignore_msg_time: boolean = false; + backend_config_fields: string[] = []; + backend_editable_fields: string[] = []; + constructor() { super(); - this.map_display.map2d.color = Psc.random_color(); - this.map_display.map3d.color = this.map_display.map2d.color; } normalize_map_display() { - this.map_display = normalize_map_display_config(this.map_display); + const display = this.map_display && typeof this.map_display === "object" ? this.map_display : {} as Data_Source_Map_Display_Config; + display.map2d = display.map2d && typeof display.map2d === "object" ? display.map2d : {}; + display.map3d = display.map3d && typeof display.map3d === "object" ? display.map3d : {}; + this.map_display = display; } map_style(mode: Map_Display_Mode): Data_Source_Map_Display_Data { return this.map_display[mode]; @@ -129,40 +85,15 @@ export class Data_Source extends enhance.Base { confirm() { this.normalize_map_display() - const payload: Record = { - enable: this.enable, - config: { - base_station_has_valid_position: this.base_station_has_valid_position, - map_display: this.map_display, - lat: this.lat, - lon: this.lon, - alt: this.alt, - ignore_msg_time: this.ignore_msg_time, - update_form_gps: this.update_form_gps, - keep_mode: this.keep_mode + const payload: Record = {} + const value = this as any; + for (const field of this.backend_editable_fields) { + if (field === "config") { + payload.config = Object.fromEntries(this.backend_config_fields.map(name => [name, value[name]])); + } else { + payload[field] = value[field]; } } - const value = this as any - if (this.type === "TCP_Client_Data_Source") { - payload.ip = value.ip - payload.port = value.port - } else if (this.type === "Serial_Data_Source") { - payload.port_name = value.port_name - payload.baud_rate = value.baud_rate - } else if (this.type === "File_Data_Source") { - payload.file_path = value.file_path - payload.data_type = value.data_type - payload.play_mode = value.play_mode - } else if (this.type === "Dll_Data_Source") { - payload.library_path = value.library_path - payload.function_name = value.function_name - payload.data_type = value.data_type - payload.buffer_size = value.buffer_size - } else if (this.type === "Shared_Memory_Data_Source") { - payload.shared_memory_name = value.shared_memory_name - payload.shared_memory_size = value.shared_memory_size - payload.data_type = value.data_type - } axios.patch(`/api/adminive/config/data_sources/${this.adminive_id}`, payload).then(() => { this.refresh_display("map2d") this.refresh_display("map3d") @@ -174,7 +105,9 @@ export class Data_Source extends enhance.Base { } toJSON() { - const {active_aircraft, base_station, aircraftMap, manual_track_icao_set, monitor_all_aircraft_mode, connect_list, first, ...rest} = this; + const {active_aircraft, base_station, aircraftMap, manual_track_icao_set, monitor_all_aircraft_mode, connect_list, first, backend_config_fields, backend_editable_fields, ...rest} = this; + void backend_config_fields; + void backend_editable_fields; return rest; // 返回去除这些属性后的对象 } locate_base_station() { diff --git a/src/Data_Source/Data_Source_Config.tsx b/src/Data_Source/Data_Source_Config.tsx index 08cad4b..5d51067 100644 --- a/src/Data_Source/Data_Source_Config.tsx +++ b/src/Data_Source/Data_Source_Config.tsx @@ -7,6 +7,11 @@ import { Shared_Memory_Data_Source, TCP_Client_Data_Source } from "./Data_Source.tsx" +import type { + Backend_Disabled_Rule, + Backend_Field_Descriptor, + Backend_Form_Section +} from "../Adminive/Backend_Fields.tsx" type Adminive_Data_Source_Row = { id: number @@ -17,6 +22,19 @@ type Adminive_Data_Source_Row = { [key: string]: unknown } +export type Data_Source_Sidebar_Schema = { + title?: string + common: Backend_Form_Section + position: Backend_Form_Section + display: Record<"map2d" | "map3d", Backend_Form_Section> + rules?: Backend_Disabled_Rule[] + tiles: Backend_Form_Section & {descriptor_fields: Backend_Field_Descriptor[]} + view3d: { + graphics: Backend_Form_Section & {descriptor_fields: Backend_Field_Descriptor[]} + map_view: Backend_Form_Section & {descriptor_fields: Backend_Field_Descriptor[]} + } +} + function create_data_source(type: string): Data_Source { if (type === "Serial_Data_Source") return new Serial_Data_Source() @@ -36,6 +54,9 @@ function create_data_source(type: string): Data_Source { export class Data_Source_Config { private sources: Data_Source[] = [] + descriptor_fields: Backend_Field_Descriptor[] = [] + config_descriptor_fields: Backend_Field_Descriptor[] = [] + sidebar_schema: Data_Source_Sidebar_Schema | null = null loading = true error = "" @@ -58,13 +79,24 @@ export class Data_Source_Config { async refresh() { try { - const response = await axios.get("/api/adminive/config/data_sources", { - params: {page: 1, perPage: 1000} - }) + const [response, descriptor_response] = await Promise.all([ + axios.get("/api/adminive/config/data_sources", {params: {page: 1, perPage: 1000}}), + axios.get("/api/adminive/config/data_sources/descriptor") + ]) const rows = response.data?.data?.items if (!Array.isArray(rows)) { throw new Error("数据源接口未返回 data.items 数组") } + const descriptor = descriptor_response.data?.data + if (!descriptor || !Array.isArray(descriptor.fields) || !descriptor.sidebar) { + throw new Error("数据源接口未返回后端字段与右侧栏描述") + } + this.descriptor_fields = descriptor.fields + this.config_descriptor_fields = descriptor.fields.find((field: Backend_Field_Descriptor) => field.name === "config")?.children || [] + this.sidebar_schema = descriptor.sidebar + const editable_fields = descriptor.fields + .filter((field: Backend_Field_Descriptor) => field.editable) + .map((field: Backend_Field_Descriptor) => field.name) const previous_sources = new Map(this.sources.map(source => [source.key, source])) this.sources = (rows as Adminive_Data_Source_Row[]).map(row => { @@ -74,6 +106,8 @@ export class Data_Source_Config { void state Object.assign(source, config ?? {}, data) source.adminive_id = id + source.backend_config_fields = Object.keys(config ?? {}) + source.backend_editable_fields = editable_fields source.normalize_map_display() return source }) diff --git a/src/Data_Source/Data_Source_Show.tsx b/src/Data_Source/Data_Source_Show.tsx index f6fd217..6abb700 100644 --- a/src/Data_Source/Data_Source_Show.tsx +++ b/src/Data_Source/Data_Source_Show.tsx @@ -3,11 +3,7 @@ import {Data_Source_Config} from "./Data_Source_Config.tsx"; import { Button, Card, - Checkbox, - CheckboxChangeEvent, - ColorPicker, Dropdown, - InputNumber, type MenuProps, Select, Space, @@ -17,14 +13,13 @@ import { Typography } from "antd"; import React from "react"; -import {Data_Source, type Data_Source_Map_Display_Data, type Map_Display_Mode} from "./Data_Source.tsx"; +import {Data_Source, type Map_Display_Mode} from "./Data_Source.tsx"; import {Base_Drawer} from "../Base_Drawer.tsx"; import {Camera_Control_Mode} from "../Map/Camera_Control_Mode.ts"; import {app} from "../App.tsx"; import {lightenColor} from "../Global.tsx"; -import {default_cesium_graphics_config} from "../Map/Cesium_Graphics_Config.ts"; import {tile_source_options, type Map_Tile_Type} from "../Map/Map_Resources.tsx"; -import {default_map_view_config} from "../Map/Map_View.tsx"; +import {Backend_Fields} from "../Adminive/Backend_Fields.tsx"; const {Title, Text} = Typography; @@ -36,30 +31,6 @@ export class Data_Source_Show extends enhance.Base { active_data_source_key = "" active_tab_key: Settings_Tab = "source" active_tile_type: Map_Tile_Type = "imagery" - number_input_drafts: Record = {} - - number_input_value(key: string, value: number): number | null { - return Object.prototype.hasOwnProperty.call(this.number_input_drafts, key) ? this.number_input_drafts[key] : value; - } - - change_number_input(key: string, value: number | null, commit: (value: number) => void) { - this.number_input_drafts[key] = value; - if (value !== null) { - commit(value); - } - this.flush(); - } - - blur_number_input(key: string) { - if (Object.prototype.hasOwnProperty.call(this.number_input_drafts, key)) { - delete this.number_input_drafts[key]; - this.flush(); - } - } - - display_title(mode: Map_Display_Mode) { - return mode === "map3d" ? "3D显示配置" : "2D显示配置"; - } tile_level_range_text(minimum: number | null | undefined, maximum: number | null | undefined): string { if (minimum === null || minimum === undefined || maximum === null || maximum === undefined) return "未加载"; @@ -114,132 +85,6 @@ export class Data_Source_Show extends enhance.Base { ); } - render_display_config(ds: Data_Source, title: string, mode: Map_Display_Mode) { - const style: Data_Source_Map_Display_Data = ds.map_style(mode); - const aircraft_size_key = `${ds.key}:${mode}:aircraft_size`; - const base_station_size_key = `${ds.key}:${mode}:base_station_size`; - const track_point_size_key = `${ds.key}:${mode}:track_point_size`; - const refresh = () => { - ds.refresh_display(mode); - this.flush(); - }; - return ( -
- {title} -
- - { - style.aircraft_show = e.target.checked; - refresh(); - }}>展示飞机 - { - style.base_station_show = e.target.checked; - refresh(); - }}>展示基站 - {mode === "map3d" && - { - style.constant_screen_size = e.target.checked; - refresh(); - }}>固定视觉大小} - - - { - style.show_icao = e.target.checked; - refresh(); - }}>显示icao - { - style.show_call_sign = e.target.checked; - refresh(); - }}>显示call_sign - { - style.show_fly_status = e.target.checked; - refresh(); - }}>显示飞行状态 - -
-
- {mode === "map2d" ? { - this.change_number_input(aircraft_size_key, value, (value) => { - style.aircraft_pixel_size = value; - refresh(); - }); - }} - onBlur={() => this.blur_number_input(aircraft_size_key)}/> : - { - this.change_number_input(aircraft_size_key, value, (value) => { - style.aircraft_scale = value; - refresh(); - }); - }} - onBlur={() => this.blur_number_input(aircraft_size_key)}/>} -
- {mode === "map2d" ? { - this.change_number_input(base_station_size_key, value, (value) => { - style.base_station_pixel_size = value; - refresh(); - }); - }} - onBlur={() => this.blur_number_input(base_station_size_key)}/> : - { - this.change_number_input(base_station_size_key, value, (value) => { - style.base_station_scale = value; - refresh(); - }); - }} - onBlur={() => this.blur_number_input(base_station_size_key)}/>} -
- { - this.change_number_input(track_point_size_key, value, (value) => { - style.track_point_pixel_size = value; - refresh(); - }); - }} - onBlur={() => this.blur_number_input(track_point_size_key)}/> -
- - 飞机标注颜色 - { - style.color = value.toHexString(); - refresh(); - }}/> - -
- - 轨迹点颜色 - { - style.track_point_color = value.toHexString(); - refresh(); - }}/> - -
- - 基站标注颜色 - { - style.base_station_color = value.toHexString(); - refresh(); - }}/> - -
- ); - } - render_aircraft_monitor_config(ds: Data_Source, mode: Map_Display_Mode) { const manual_list = ds.manual_track_icao_list(); return ( @@ -395,10 +240,10 @@ export class Data_Source_Show extends enhance.Base { ] : [{value: "imagery", label: "影像瓦片"}]; const source_options = tile_source_options(map?.map_resources || null, tile_type); const source_key = this.selected_tile_source_key(map, tile_type, source_options); - const display_level = map?.tile_display_level_draft !== undefined ? map.tile_display_level_draft : tile_view?.tile_display_maximum_level; + const tile_schema = this.data_source_config?.sidebar_schema?.tiles; return (
- 瓦片显示 + {tile_schema?.title ?? "瓦片显示"} - this.upload("aircraft_type", file, option.key)}> - - - - {this.render_model_detail(model)} - - ); - } - render_model_upload(label: string, model: Map_Model_Item_Config, model_type: "aircraft" | "base_station" | "device") { - return ( - - - - this.upload(model_type, file)}> - - - - {this.render_model_detail(model, model_type === "base_station" ? "模型原始高度" : "内置大小")} + +

{title}

+ field.name)}} + onChange={() => this.flush()}/> + this.upload(upload_schema, file, item_key)}> + +
); } + render(props: any) { return (

{this.schema.title ?? "Cesium模型设置"}

- -

默认飞机模型

- {this.render_model_upload("飞机模型", this.config.aircraftModel, "aircraft")} -

基站模型

- {this.render_model_upload("基站模型", this.config.baseStationModel, "base_station")} -

设备模型

- {this.render_model_upload("设备模型", this.config.deviceModel, "device")} -

按涡流/目标类型选择飞机模型

- {this.schema.aircraft_types.map(key => this.render_aircraft_type_model(aircraft_model_type_options.find(option => option.key === key) ?? {key, label: key}))} + + {(this.schema.groups || []).map(group => + this.render_model(group.title, group.path, group.upload))} + {(this.schema.collections || []).flatMap(collection => [ +

{collection.title}

, + ...collection.items.map(item => this.render_model(item.label, `${collection.path}.${item.key}`, { + model_type: collection.upload_model_type, + label: collection.upload_label || "上传模型", + success_message: collection.upload_success_message + }, item.key)) + ])}
diff --git a/src/Map/Map_Models.tsx b/src/Map/Map_Models.tsx index 129d642..69b78c8 100644 --- a/src/Map/Map_Models.tsx +++ b/src/Map/Map_Models.tsx @@ -1,139 +1,32 @@ import axios from "axios"; -export type Aircraft_Model_Type_Option = { - key: string - label: string - modelFile: string -} -export const aircraft_model_type_options: Aircraft_Model_Type_Option[] = [ - {key: "no_category_information", label: "无类别信息", modelFile: "aircraft-no-category-information.glb"}, - {key: "surface_emergency_vehicle", label: "地面应急车", modelFile: "aircraft-surface-emergency-vehicle.glb"}, - {key: "surface_service_vehicle", label: "地面服务车", modelFile: "aircraft-surface-service-vehicle.glb"}, - {key: "ground_obstruction_4", label: "地面障碍物4", modelFile: "aircraft-ground-obstruction-4.glb"}, - {key: "ground_obstruction_5", label: "地面障碍物5", modelFile: "aircraft-ground-obstruction-5.glb"}, - {key: "ground_obstruction_6", label: "地面障碍物6", modelFile: "aircraft-ground-obstruction-6.glb"}, - {key: "ground_obstruction_7", label: "地面障碍物7", modelFile: "aircraft-ground-obstruction-7.glb"}, - {key: "glider", label: "滑翔机,滑翔飞机", modelFile: "aircraft-glider.glb"}, - {key: "lighter_than_air", label: "比空气轻", modelFile: "aircraft-lighter-than-air.glb"}, - {key: "parachutist", label: "跳伞运动员", modelFile: "aircraft-parachutist.glb"}, - {key: "ultralight_hangglider_paraglider", label: "超轻型,悬挂式滑翔机,滑翔伞", modelFile: "aircraft-ultralight-hangglider-paraglider.glb"}, - {key: "reserved_3_5", label: "保留", modelFile: "aircraft-reserved-3-5.glb"}, - {key: "unmanned_aerial_vehicle", label: "无人机", modelFile: "aircraft-unmanned-aerial-vehicle.glb"}, - {key: "space_transatmospheric_vehicle", label: "太空或跨大气层飞行器", modelFile: "aircraft-space-transatmospheric-vehicle.glb"}, - {key: "light_aircraft", label: "轻型飞机(小于 7000 公斤)", modelFile: "aircraft-light-aircraft.glb"}, - {key: "medium_1_aircraft", label: "中型 1(7000 公斤到 34000 公斤之间)", modelFile: "aircraft-medium-1-aircraft.glb"}, - {key: "medium_2_aircraft", label: "中型 2(34000 公斤到 136000 公斤之间)", modelFile: "aircraft-medium-2-aircraft.glb"}, - {key: "high_vortex_aircraft", label: "高涡旋飞机", modelFile: "aircraft-high-vortex-aircraft.glb"}, - {key: "heavy_aircraft", label: "重型飞机(大于 136000 公斤)", modelFile: "aircraft-heavy-aircraft.glb"}, - {key: "high_performance_aircraft", label: "高性能(>5 g 加速度)和高速(>400 节)", modelFile: "aircraft-high-performance-aircraft.glb"}, - {key: "rotorcraft", label: "旋翼机", modelFile: "aircraft-rotorcraft.glb"} -]; -export type Map_Model_Item_Config = { - url: string - builtInSize: number - headingOffsetDegrees: number - pitchOffsetDegrees: number - rollOffsetDegrees: number -} -export type Map_Model_Config = { - aircraftModel: Map_Model_Item_Config - aircraftModels: Record - baseStationModel: Map_Model_Item_Config - deviceModel: Map_Model_Item_Config -} -type Map_Model_Item_Config_Json = { - url: string - built_in_size: number - heading_offset_degrees: number - pitch_offset_degrees: number - roll_offset_degrees: number -} -type Map_Model_Config_Json = { - aircraft_model: Map_Model_Item_Config_Json - aircraft_models?: Record - base_station_model: Map_Model_Item_Config_Json - device_model: Map_Model_Item_Config_Json -} -function default_model_item(url: string, builtInSize: number, heading = 0, pitch = 0, roll = 0): Map_Model_Item_Config { - return {url, builtInSize, headingOffsetDegrees: heading, pitchOffsetDegrees: pitch, rollOffsetDegrees: roll}; -} -function default_aircraft_models(): Record { - return Object.fromEntries(aircraft_model_type_options.map(item => [item.key, default_model_item(`/ui/model/${item.modelFile}`, 50, -90)])); -} -export const empty_map_model_config: Map_Model_Config = { - aircraftModel: default_model_item("/ui/model/aircraft.glb", 50, -90), - aircraftModels: default_aircraft_models(), - baseStationModel: default_model_item("/ui/model/base-station.glb", 16.635522), - deviceModel: default_model_item("/ui/model/device.glb", 80) -}; +export type Map_Model_Item_Config = Record +export type Map_Model_Config = Record + +export const empty_map_model_config: Map_Model_Config = {}; export const map_model_config_event = "ecap_map_model_config_changed"; -function number_or(value: unknown, fallback: number): number { - const next = Number(value); - return Number.isFinite(next) ? next : fallback; -} -function from_server_model_item(data: Partial | undefined, fallback: Map_Model_Item_Config): Map_Model_Item_Config { - return { - url: typeof data?.url === "string" ? data.url : fallback.url, - builtInSize: number_or(data?.built_in_size, fallback.builtInSize), - headingOffsetDegrees: number_or(data?.heading_offset_degrees, fallback.headingOffsetDegrees), - pitchOffsetDegrees: number_or(data?.pitch_offset_degrees, fallback.pitchOffsetDegrees), - rollOffsetDegrees: number_or(data?.roll_offset_degrees, fallback.rollOffsetDegrees) - }; -} -function to_server_model_item(config: Map_Model_Item_Config): Map_Model_Item_Config_Json { - return { - url: config.url, - built_in_size: config.builtInSize, - heading_offset_degrees: config.headingOffsetDegrees, - pitch_offset_degrees: config.pitchOffsetDegrees, - roll_offset_degrees: config.rollOffsetDegrees - }; -} -function aircraft_models_from_server(values?: Record): Record { - const models = default_aircraft_models(); - if (values) { - for (const [key, value] of Object.entries(values)) { - models[key] = from_server_model_item(value, models[key] || empty_map_model_config.aircraftModel); - } - } - return models; -} -function from_server_config(data: Partial): Map_Model_Config { - return { - aircraftModel: from_server_model_item(data.aircraft_model, empty_map_model_config.aircraftModel), - aircraftModels: aircraft_models_from_server(data.aircraft_models), - baseStationModel: from_server_model_item(data.base_station_model, empty_map_model_config.baseStationModel), - deviceModel: from_server_model_item(data.device_model, empty_map_model_config.deviceModel) - }; -} -function to_server_config(config: Map_Model_Config): Map_Model_Config_Json { - return { - aircraft_model: to_server_model_item(config.aircraftModel), - aircraft_models: Object.fromEntries(Object.entries(config.aircraftModels).map(([key, value]) => [key, to_server_model_item(value)])), - base_station_model: to_server_model_item(config.baseStationModel), - device_model: to_server_model_item(config.deviceModel) - }; -} + export async function load_map_model_config(api = "/map/models"): Promise { - const response = await axios.get(api); - return from_server_config(response.data); + const response = await axios.get(api); + return response.data; } + export async function save_map_model_config(config: Map_Model_Config, api = "/map/models"): Promise { - const response = await axios.post(api, to_server_config(config)); - const saved_config = from_server_config(response.data); - emit_map_model_config(saved_config); - return saved_config; + const response = await axios.post(api, config); + emit_map_model_config(response.data); + return response.data; } -export async function upload_map_model(model_type: "aircraft" | "base_station" | "device" | "aircraft_type", file: File, aircraft_model_key?: string, api = "/map/models"): Promise { + +export async function upload_map_model(model_type: string, file: File, aircraft_model_key?: string, api = "/map/models"): Promise { const form = new FormData(); form.append("model_type", model_type); if (aircraft_model_key) form.append("aircraft_model_key", aircraft_model_key); form.append("file", file); - const response = await axios.post(api, form); - const config = from_server_config(response.data); - emit_map_model_config(config); - return config; + const response = await axios.post(api, form); + emit_map_model_config(response.data); + return response.data; } + export function emit_map_model_config(config: Map_Model_Config) { window.dispatchEvent(new CustomEvent(map_model_config_event, {detail: config})); } diff --git a/src/Map/Map_View.tsx b/src/Map/Map_View.tsx index 901c94f..bda8fe7 100644 --- a/src/Map/Map_View.tsx +++ b/src/Map/Map_View.tsx @@ -1,98 +1,19 @@ import axios from "axios"; export type Scene_Mode_Key = "2d" | "2.5d" | "3d" -export type Map_Camera_View = { - longitude: number - latitude: number - height: number - heading: number - pitch: number - roll: number -} -export type Map_Tile_View_Config = { - current_imagery_key: string - current_terrain_key: string - tile_zoom_mode: "native" | "upscale" | "both" - tile_display_maximum_level: number -} -export type Map_View_Config = { - current_imagery_key: string - scene_mode: Scene_Mode_Key - map2d: Map_Tile_View_Config - map3d: Map_Tile_View_Config - camera: Map_Camera_View - baseStationFlyToHeightOffsetMeters: number -} -export const default_map_camera_view: Map_Camera_View = { - longitude: 121.27, - latitude: 37.41, - height: 300000, - heading: 0, - pitch: -90, - roll: 0 -}; -export const default_map_view_config: Map_View_Config = { - current_imagery_key: "imagery", - scene_mode: "3d", - map2d: { - current_imagery_key: "imagery", - current_terrain_key: "terrain", - tile_zoom_mode: "native", - tile_display_maximum_level: 19 - }, - map3d: { - current_imagery_key: "imagery", - current_terrain_key: "terrain", - tile_zoom_mode: "native", - tile_display_maximum_level: 19 - }, - camera: {...default_map_camera_view}, - baseStationFlyToHeightOffsetMeters: 8000 -}; -function number_value(value: unknown, fallback: number): number { - const next = Number(value); - return Number.isFinite(next) ? next : fallback; -} -function scene_mode_value(value: unknown, fallback: Scene_Mode_Key): Scene_Mode_Key { - return value === "2d" || value === "2.5d" || value === "3d" ? value : fallback; -} -function tile_zoom_mode_value(value: unknown, fallback: Map_Tile_View_Config["tile_zoom_mode"]): Map_Tile_View_Config["tile_zoom_mode"] { - return value === "native" || value === "upscale" || value === "both" ? value : fallback; -} -function normalize_tile_view_config(data: Partial | undefined, fallback: Map_Tile_View_Config): Map_Tile_View_Config { - return { - current_imagery_key: typeof data?.current_imagery_key === "string" ? data.current_imagery_key : fallback.current_imagery_key, - current_terrain_key: typeof data?.current_terrain_key === "string" ? data.current_terrain_key : fallback.current_terrain_key, - tile_zoom_mode: tile_zoom_mode_value(data?.tile_zoom_mode, fallback.tile_zoom_mode), - tile_display_maximum_level: Math.max(0, Math.min(24, number_value(data?.tile_display_maximum_level, fallback.tile_display_maximum_level))) - }; -} -function normalize_camera(data: Partial | undefined): Map_Camera_View { - return { - longitude: number_value(data?.longitude, default_map_camera_view.longitude), - latitude: number_value(data?.latitude, default_map_camera_view.latitude), - height: number_value(data?.height, default_map_camera_view.height), - heading: number_value(data?.heading, default_map_camera_view.heading), - pitch: number_value(data?.pitch, default_map_camera_view.pitch), - roll: number_value(data?.roll, default_map_camera_view.roll) - }; -} -export function normalize_map_view_config(data: Partial): Map_View_Config { - return { - current_imagery_key: typeof data.current_imagery_key === "string" ? data.current_imagery_key : default_map_view_config.current_imagery_key, - scene_mode: scene_mode_value(data.scene_mode, default_map_view_config.scene_mode), - map2d: normalize_tile_view_config(data.map2d, default_map_view_config.map2d), - map3d: normalize_tile_view_config(data.map3d, default_map_view_config.map3d), - camera: normalize_camera(data.camera), - baseStationFlyToHeightOffsetMeters: Math.max(100, number_value(data.baseStationFlyToHeightOffsetMeters, default_map_view_config.baseStationFlyToHeightOffsetMeters)) - }; -} +export type Map_Camera_View = Record +export type Map_Tile_View_Config = Record +export type Map_View_Config = Record + +export const default_map_camera_view: Map_Camera_View = {}; +export const default_map_view_config: Map_View_Config = {map2d: {}, map3d: {}, camera: {}}; + export async function load_map_view_config(): Promise { const response = await axios.get("/map/view"); - return normalize_map_view_config(response.data); + return response.data; } + export async function save_map_view_config(config: Map_View_Config): Promise { - const normalized = normalize_map_view_config(config); - const response = await axios.post("/map/view", normalized); - return normalize_map_view_config(response.data); + const response = await axios.post("/map/view", config); + return response.data; }