feat: extend supply workbench demo and map navigation
This commit is contained in:
@@ -50,6 +50,7 @@ import {
|
||||
scadaInteractionLayers
|
||||
} from "../map/scada";
|
||||
import { registerSupplyAssetImages } from "../map/supply-icons";
|
||||
import { installCityScene } from "../map/city-scene-control";
|
||||
import { useMapInteractions } from "./use-map-interactions";
|
||||
import { useSimulationLayerVisibility } from "./use-simulation-layer-visibility";
|
||||
|
||||
@@ -79,7 +80,10 @@ type UseWorkbenchMapResult = {
|
||||
sourceStatuses: WorkbenchSourceStatus[];
|
||||
availableSourceIds: WaterNetworkSourceId[];
|
||||
sourceFeatureCounts: Partial<Record<WaterNetworkSourceId, number>>;
|
||||
getScadaFeatures: (deviceIds?: string[], signal?: AbortSignal) => Promise<FeatureCollection<Point, ScadaDevice>>;
|
||||
getScadaFeatures: (
|
||||
deviceIds?: string[],
|
||||
signal?: AbortSignal
|
||||
) => Promise<FeatureCollection<Point, ScadaDevice>>;
|
||||
scadaObservedAt: string | null;
|
||||
refreshGeoServerLayers: () => boolean;
|
||||
fitNetworkBounds: () => void;
|
||||
@@ -96,6 +100,7 @@ export type WorkbenchSourceStatus = {
|
||||
|
||||
const SOURCE_STATUS_LABELS: Record<string, string> = {
|
||||
"mapbox-base": "Mapbox 底图",
|
||||
"tianditu-base": "天地图底图",
|
||||
"geoserver-mvt": "GeoServer MVT",
|
||||
"geoserver-catalog": "GeoServer 图层目录",
|
||||
"scada-api": "SCADA API",
|
||||
@@ -106,6 +111,10 @@ const SOURCE_STATUS_LABELS: Record<string, string> = {
|
||||
const SOURCE_GROUP_BY_ID: Record<string, string> = {
|
||||
"mapbox-light": "mapbox-base",
|
||||
"mapbox-satellite": "mapbox-base",
|
||||
"tianditu-vector": "tianditu-base",
|
||||
"tianditu-vector-annotation": "tianditu-base",
|
||||
"tianditu-image": "tianditu-base",
|
||||
"tianditu-image-annotation": "tianditu-base",
|
||||
...Object.fromEntries(
|
||||
GEOSERVER_WATER_NETWORK_SOURCE_IDS.map((sourceId) => [sourceId, "geoserver-mvt"])
|
||||
),
|
||||
@@ -197,9 +206,11 @@ export function useWorkbenchMap({
|
||||
? current
|
||||
: availability.availableSourceIds
|
||||
);
|
||||
setSourceFeatureCounts((current) => areFeatureCountsEqual(current, availability.featureCounts)
|
||||
? current
|
||||
: availability.featureCounts);
|
||||
setSourceFeatureCounts((current) =>
|
||||
areFeatureCountsEqual(current, availability.featureCounts)
|
||||
? current
|
||||
: availability.featureCounts
|
||||
);
|
||||
|
||||
if (availability.failedSourceIds.length > 0) {
|
||||
updateSourceStatus(
|
||||
@@ -251,12 +262,13 @@ export function useWorkbenchMap({
|
||||
setMapReady(false);
|
||||
setMapError(null);
|
||||
const mapboxToken = env.TJWATER_MAPBOX_ACCESS_TOKEN || undefined;
|
||||
const tiandituToken = env.TJWATER_TIANDITU_TOKEN || undefined;
|
||||
const scadaAbortController = new AbortController();
|
||||
let scadaRefreshTimeoutId: number | undefined;
|
||||
updateSourceStatus(setSourceStatuses, "scada-api", "loading", "正在通过API加载SCADA设备。");
|
||||
const map = new maplibregl.Map({
|
||||
container: containerRef.current,
|
||||
style: createBaseStyle(mapboxToken),
|
||||
style: createBaseStyle(mapboxToken, tiandituToken),
|
||||
pitch: 0,
|
||||
bearing: 0,
|
||||
maxZoom: MAP_MAX_ZOOM,
|
||||
@@ -265,9 +277,11 @@ export function useWorkbenchMap({
|
||||
});
|
||||
|
||||
window.__waterNetworkMap = map;
|
||||
let removeCityScene: (() => void) | undefined;
|
||||
|
||||
map.on("load", async () => {
|
||||
map.resize();
|
||||
removeCityScene = installCityScene(map);
|
||||
(map.getSource(SCADA_SOURCE_ID) as GeoJSONSource | undefined)?.setData(
|
||||
scadaCollectionRef.current
|
||||
);
|
||||
@@ -279,11 +293,7 @@ export function useWorkbenchMap({
|
||||
map.addSource(SIMULATION_SOURCE_IDS.impactArea, simulationSources.impactArea);
|
||||
map.addSource(SIMULATION_SOURCE_IDS.annotations, simulationSources.annotations);
|
||||
map.addSource(SIMULATION_SOURCE_IDS.backendAnalysis, simulationSources.backendAnalysis);
|
||||
syncSimulationSourceData(
|
||||
map,
|
||||
simulationOverlayRef.current,
|
||||
allowDemoSimulationRef.current
|
||||
);
|
||||
syncSimulationSourceData(map, simulationOverlayRef.current, allowDemoSimulationRef.current);
|
||||
await registerSupplyAssetImages(map);
|
||||
const scadaRegistration = await registerScadaImages(map);
|
||||
const activeScadaBusinessLayers =
|
||||
@@ -340,10 +350,7 @@ export function useWorkbenchMap({
|
||||
});
|
||||
|
||||
const refreshScada = async () => {
|
||||
const snapshotPromise = fetchScadaSnapshot(
|
||||
getAccessToken,
|
||||
scadaAbortController.signal
|
||||
);
|
||||
const snapshotPromise = fetchScadaSnapshot(getAccessToken, scadaAbortController.signal);
|
||||
scadaLoadPromiseRef.current = snapshotPromise
|
||||
.then((snapshot) => snapshot.collection)
|
||||
.catch(() => scadaCollectionRef.current);
|
||||
@@ -428,6 +435,7 @@ export function useWorkbenchMap({
|
||||
if (scadaRefreshTimeoutId !== undefined) {
|
||||
window.clearTimeout(scadaRefreshTimeoutId);
|
||||
}
|
||||
removeCityScene?.();
|
||||
map.remove();
|
||||
mapRef.current = null;
|
||||
delete window.__waterNetworkMap;
|
||||
@@ -500,8 +508,10 @@ function areFeatureCountsEqual(
|
||||
) {
|
||||
const currentEntries = Object.entries(current);
|
||||
const nextEntries = Object.entries(next);
|
||||
return currentEntries.length === nextEntries.length
|
||||
&& nextEntries.every(([sourceId, count]) => current[sourceId as WaterNetworkSourceId] === count);
|
||||
return (
|
||||
currentEntries.length === nextEntries.length &&
|
||||
nextEntries.every(([sourceId, count]) => current[sourceId as WaterNetworkSourceId] === count)
|
||||
);
|
||||
}
|
||||
|
||||
function networkViewKey(view: NetworkViewParams) {
|
||||
@@ -549,8 +559,9 @@ function syncDataDependentGeoServerLayers(
|
||||
|
||||
DATA_DEPENDENT_GEOSERVER_SOURCE_IDS.forEach((sourceId) => {
|
||||
if (availableSourceIdSet.has(sourceId)) return;
|
||||
map.getStyle().layers
|
||||
.filter((layer) => "source" in layer && layer.source === sourceId)
|
||||
map
|
||||
.getStyle()
|
||||
.layers.filter((layer) => "source" in layer && layer.source === sourceId)
|
||||
.reverse()
|
||||
.forEach((layer) => map.removeLayer(layer.id));
|
||||
if (map.getSource(sourceId)) map.removeSource(sourceId);
|
||||
@@ -711,11 +722,15 @@ function getSourceGroupFromErrorEvent(event: { sourceId?: string; error?: { mess
|
||||
return "mapbox-base";
|
||||
}
|
||||
|
||||
if (message.includes("tianditu.gov.cn") || message.includes("tianditu")) {
|
||||
return "tianditu-base";
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function getSourceStatusMessage(sourceGroupId: string, status: WorkbenchSourceStatusValue) {
|
||||
if (sourceGroupId === "mapbox-base") {
|
||||
if (["mapbox-base", "tianditu-base"].includes(sourceGroupId)) {
|
||||
return status === "online"
|
||||
? "底图瓦片已恢复正常。"
|
||||
: "底图瓦片请求中断,业务图层仍可继续使用。";
|
||||
|
||||
Reference in New Issue
Block a user