Files
planet/frontend/public/earth/js/layer-startup-tasks.js
2026-04-30 14:30:12 +08:00

304 lines
9.6 KiB
JavaScript

import {
loadGeoJSONFromPath,
loadLandingPoints,
getCableLegendItems,
toggleCables,
} from "./cables.js";
import {
clearSatelliteData,
getSatelliteLegendItems,
loadSatellites,
toggleSatellites,
} from "./satellites.js";
import {
loadBGPAnomalies,
toggleBGP,
} from "./bgp.js";
import {
loadComputeCenters,
toggleComputeCenters,
} from "./compute-centers.js";
import {
getVesselLegendItems,
loadVessels,
toggleVessels,
} from "./vessels.js";
import {
loadCloudTexture,
loadEarthTexture,
} from "./earth.js";
import {
getCountryBoundaryLegendItems,
loadCountryBoundaries,
toggleCountryBoundaries,
} from "./country-boundaries.js";
/**
* Layer startup task registry.
*
* This module is the startup-task counterpart to the layer registry in controls.js:
* - controls.js owns layer metadata such as startupPriority/startupMode/startupMessage
* - this file owns the executable startup task factory for each layer id
*
* A startup task is registered via registerLayerStartupTask(id, taskFactory).
* The taskFactory receives a startup context from main.js and must return an async
* function with the signature async (layerDefinition) => void.
*
* Put a task here only when a layer needs dedicated startup loading work:
* - preloading data at boot
* - staged loading with progress/loading messages
* - post-load UI refresh or warmup
*
* Do not put plain visibility toggles or persistent UI state here; those still belong
* to the layer registry/state flow in controls.js.
*/
const startupTaskRegistry = new Map();
export function resolveStartupMessage(layer, key, fallback) {
const message = layer?.startupMessage;
if (message && typeof message === "object" && key in message) {
return message[key];
}
if (typeof message === "string" && message.trim()) {
return message;
}
return fallback;
}
export function createLayerStartupTaskMap(context) {
return Object.fromEntries(
Array.from(startupTaskRegistry.entries()).map(([id, taskFactory]) => [
id,
taskFactory(context),
]),
);
}
export function registerLayerStartupTask(id, taskFactory) {
if (typeof id !== "string" || !id.trim()) {
throw new Error("registerLayerStartupTask 需要有效的图层 id");
}
if (typeof taskFactory !== "function") {
throw new Error("registerLayerStartupTask 需要可调用的任务工厂");
}
startupTaskRegistry.set(id, taskFactory);
}
function registerBuiltinLayerStartupTasks() {
startupTaskRegistry.clear();
registerCountryBoundaryStartupTask();
registerEarthTextureStartupTask();
registerCloudStartupTask();
registerCableStartupTask();
registerComputeCenterStartupTask();
registerVesselStartupTask();
registerBGPStartupTask();
registerSatelliteStartupTask();
}
function registerVesselStartupTask() {
registerLayerStartupTask("vessels", (context) => async (layer) => {
context.setLoadingMessage(
resolveStartupMessage(layer, "load", "正在加载船只..."),
);
await context.yieldFrame(12);
try {
const vesselResult = await loadVessels(context.scene, context.earth);
if (!context.isCancelled()) {
toggleVessels(context.getShowVessels());
context.updateVesselHud(vesselResult);
context.setLegendItems("vessels", getVesselLegendItems());
context.refreshLegend();
}
} catch (error) {
context.reportError(layer?.startupLabel || layer?.label || "船只", error);
}
if (context.isCancelled()) return;
await context.yieldFrame(16);
});
}
function registerCableStartupTask() {
registerLayerStartupTask("cables", (context) => async (layer) => {
if (!context.isCablesEnabled()) return;
context.setLoadingMessage(
resolveStartupMessage(layer, "prepare", "正在加载登陆点..."),
);
await context.yieldFrame(12);
try {
await loadLandingPoints(context.scene, context.earth, { silent: true });
} catch (error) {
context.reportError("登陆点", error);
}
if (context.isCancelled()) return;
await context.yieldFrame(16);
context.setLoadingMessage(
resolveStartupMessage(layer, "load", "正在加载海缆..."),
);
await context.yieldFrame(12);
try {
await loadGeoJSONFromPath(context.scene, context.earth, { silent: true });
if (!context.isCancelled() && context.isCablesEnabled()) {
toggleCables(true);
context.updateCableToggleUi(true);
context.setLegendItems("cables", getCableLegendItems());
context.refreshLegend();
}
} catch (error) {
context.reportError(layer?.startupLabel || layer?.label || "海缆", error);
}
if (context.isCancelled()) return;
await context.yieldFrame(16);
});
}
function registerSatelliteStartupTask() {
registerLayerStartupTask("satellites", (context) => async (layer) => {
if (!context.isSatellitesEnabled()) return;
context.setLoadingMessage(
resolveStartupMessage(layer, "load", "正在加载卫星..."),
);
await context.yieldFrame(12);
try {
clearSatelliteData();
const loadResult = await loadSatellites({
limit: context.getInitialSatelliteLoadLimit(),
});
if (!context.isCancelled() && context.isSatellitesEnabled()) {
context.updateSatelliteToggleUi(true, loadResult.count);
context.setLegendItems("satellites", getSatelliteLegendItems());
context.refreshLegend();
context.scheduleSatellitePositionWarmup(() => {
if (!context.isCancelled() && context.isSatellitesEnabled()) {
toggleSatellites(true);
}
});
if (context.shouldHydrateFullSatelliteSet(loadResult)) {
const hydrationToken = context.nextSatelliteHydrationToken();
context.hydrateAllSatellitesInBackground(
() =>
hydrationToken === context.getSatelliteHydrationToken() &&
!context.isCancelled() &&
context.isSatellitesEnabled(),
);
}
}
} catch (error) {
context.reportError(layer?.startupLabel || layer?.label || "卫星", error);
}
if (context.isCancelled()) return;
await context.yieldFrame(16);
});
}
function registerBGPStartupTask() {
registerLayerStartupTask("bgp", (context) => async (layer) => {
context.setLoadingMessage(
resolveStartupMessage(layer, "load", "正在加载BGP态势..."),
);
await context.yieldFrame(12);
try {
const bgpResult = await loadBGPAnomalies(context.scene, context.earth);
if (!context.isCancelled()) {
toggleBGP(context.getShowBGP());
context.updateBGPHud(bgpResult);
context.syncBGPKnownEventIds();
}
} catch (error) {
context.reportError(layer?.startupLabel || layer?.label || "BGP态势", error);
}
if (context.isCancelled()) return;
await context.yieldFrame(16);
});
}
function registerEarthTextureStartupTask() {
registerLayerStartupTask("earthHighResTexture", (context) => async (layer) => {
if (!context.isEarthTextureVisible()) return;
context.setLoadingMessage(
resolveStartupMessage(layer, "load", "正在加载地球纹理..."),
);
await context.yieldFrame(12);
try {
await loadEarthTexture();
} catch (error) {
console.warn("地球纹理加载失败:", error);
}
if (context.isCancelled()) return;
await context.yieldFrame(16);
});
}
function registerCloudStartupTask() {
registerLayerStartupTask("atmosphereClouds", (context) => async (layer) => {
if (!context.isCloudsEnabled()) return;
context.setLoadingMessage(
resolveStartupMessage(layer, "load", "正在加载大气云图..."),
);
await context.yieldFrame(12);
try {
await loadCloudTexture();
} catch (error) {
context.reportError(layer?.startupLabel || layer?.label || "大气云图", error);
}
if (context.isCancelled()) return;
await context.yieldFrame(16);
});
}
function registerCountryBoundaryStartupTask() {
registerLayerStartupTask("countryBoundaries", (context) => async (layer) => {
context.setLoadingMessage(
resolveStartupMessage(layer, "load", "正在加载海陆基座..."),
);
await context.yieldFrame(12);
try {
await loadCountryBoundaries();
if (!context.isCancelled()) {
const textureOn = context.isEarthTextureVisible();
toggleCountryBoundaries(context.getShowCountryBoundaries(), {
showTint: !textureOn,
showLandFill: true,
suppressLandFill: false,
});
context.setLegendItems("countryBoundaries", getCountryBoundaryLegendItems());
context.refreshLegend();
}
} catch (error) {
context.reportError(layer?.startupLabel || layer?.label || "国界", error);
}
if (context.isCancelled()) return;
await context.yieldFrame(16);
});
}
function registerComputeCenterStartupTask() {
registerLayerStartupTask("computeCenters", (context) => async (layer) => {
context.setLoadingMessage(
resolveStartupMessage(layer, "load", "正在加载算力中心..."),
);
await context.yieldFrame(12);
try {
const computeCenterResult = await loadComputeCenters(context.scene, context.earth);
if (!context.isCancelled()) {
toggleComputeCenters(context.getShowComputeCenters());
context.updateComputeCenterHud(computeCenterResult);
context.setLegendItems("computeCenters", context.getComputeCenterLegendItems());
context.refreshLegend();
}
} catch (error) {
context.reportError(layer?.startupLabel || layer?.label || "算力中心", error);
}
if (context.isCancelled()) return;
await context.yieldFrame(16);
});
}
registerBuiltinLayerStartupTasks();