444 lines
12 KiB
TypeScript
444 lines
12 KiB
TypeScript
import type { StyleSpecification, VectorSourceSpecification } from "maplibre-gl";
|
|
|
|
import { GEOSERVER_WORKSPACE, MAP_URL } from "./geoserver-config";
|
|
import { MAP_STYLE_TOKENS } from "./map-colors";
|
|
import { SCADA_SOURCE_ID } from "./scada";
|
|
|
|
export const WATER_NETWORK_GLOBAL_VIEW = {
|
|
bbox3857: [13508801.930066336, 3608163.3499832638, 13555650.638648884, 3633685.137885742]
|
|
} as const;
|
|
|
|
export const SOURCE_LAYERS = {
|
|
pipes: "pipes",
|
|
junctions: "junctions",
|
|
valves: "valves",
|
|
reservoirs: "reservoirs",
|
|
pumps: "pumps",
|
|
tanks: "tanks",
|
|
water_quality_monitoring_points: "water_quality_monitoring_points",
|
|
fx_20200423_drain_valves: "fx_20200423_drain_valves",
|
|
fx_20200423_service_connections: "fx_20200423_service_connections",
|
|
fx_20200423_flow_meters: "fx_20200423_flow_meters",
|
|
fx_20200423_hydrants: "fx_20200423_hydrants",
|
|
fx_20200423_customer_meters: "fx_20200423_customer_meters",
|
|
fx_20200423_pipes: "fx_20200423_pipes",
|
|
fx_20200423_valves: "fx_20200423_valves",
|
|
fx_20200423_non_residential_meters: "fx_20200423_non_residential_meters",
|
|
fx_20200811_dn100_pipes: "fx_20200811_dn100_pipes"
|
|
} as const;
|
|
|
|
export const WATER_NETWORK_SOURCE_IDS = [
|
|
"pipes",
|
|
"junctions",
|
|
"valves",
|
|
"reservoirs",
|
|
"scada",
|
|
"pumps",
|
|
"tanks",
|
|
"water_quality_monitoring_points",
|
|
"fx_20200423_drain_valves",
|
|
"fx_20200423_service_connections",
|
|
"fx_20200423_flow_meters",
|
|
"fx_20200423_hydrants",
|
|
"fx_20200423_customer_meters",
|
|
"fx_20200423_pipes",
|
|
"fx_20200423_valves",
|
|
"fx_20200423_non_residential_meters",
|
|
"fx_20200811_dn100_pipes"
|
|
] as const;
|
|
|
|
export type WaterNetworkSourceId = (typeof WATER_NETWORK_SOURCE_IDS)[number];
|
|
|
|
export type SupplyLayerCatalogItem = {
|
|
id: WaterNetworkSourceId;
|
|
sourceLayer?: (typeof SOURCE_LAYERS)[keyof typeof SOURCE_LAYERS];
|
|
geometry: "line" | "point";
|
|
available: boolean;
|
|
availability: "required" | "probe";
|
|
label: string;
|
|
icon: string;
|
|
group: "model" | "monitoring" | "source-gis";
|
|
initialVisible: boolean;
|
|
};
|
|
|
|
export const SUPPLY_LAYER_CATALOG = [
|
|
{
|
|
id: "pipes",
|
|
sourceLayer: SOURCE_LAYERS.pipes,
|
|
geometry: "line",
|
|
available: true,
|
|
availability: "required",
|
|
label: "管线",
|
|
icon: "pipe",
|
|
group: "model",
|
|
initialVisible: true
|
|
},
|
|
{
|
|
id: "junctions",
|
|
sourceLayer: SOURCE_LAYERS.junctions,
|
|
geometry: "point",
|
|
available: true,
|
|
availability: "required",
|
|
label: "节点",
|
|
icon: "junction",
|
|
group: "model",
|
|
initialVisible: true
|
|
},
|
|
{
|
|
id: "valves",
|
|
sourceLayer: SOURCE_LAYERS.valves,
|
|
geometry: "point",
|
|
available: true,
|
|
availability: "probe",
|
|
label: "阀门",
|
|
icon: "valve",
|
|
group: "model",
|
|
initialVisible: true
|
|
},
|
|
{
|
|
id: "reservoirs",
|
|
sourceLayer: SOURCE_LAYERS.reservoirs,
|
|
geometry: "point",
|
|
available: true,
|
|
availability: "probe",
|
|
label: "水库",
|
|
icon: "reservoir",
|
|
group: "model",
|
|
initialVisible: true
|
|
},
|
|
{
|
|
id: "scada",
|
|
sourceLayer: undefined,
|
|
geometry: "point",
|
|
available: true,
|
|
availability: "required",
|
|
label: "SCADA",
|
|
icon: "scada-pressure",
|
|
group: "monitoring",
|
|
initialVisible: true
|
|
},
|
|
{
|
|
id: "pumps",
|
|
sourceLayer: SOURCE_LAYERS.pumps,
|
|
geometry: "point",
|
|
available: true,
|
|
availability: "probe",
|
|
label: "水泵",
|
|
icon: "pump",
|
|
group: "model",
|
|
initialVisible: true
|
|
},
|
|
{
|
|
id: "tanks",
|
|
sourceLayer: SOURCE_LAYERS.tanks,
|
|
geometry: "point",
|
|
available: true,
|
|
availability: "probe",
|
|
label: "水箱",
|
|
icon: "tank",
|
|
group: "model",
|
|
initialVisible: true
|
|
},
|
|
{
|
|
id: "water_quality_monitoring_points",
|
|
sourceLayer: SOURCE_LAYERS.water_quality_monitoring_points,
|
|
geometry: "point",
|
|
available: true,
|
|
availability: "probe",
|
|
label: "水质监测点",
|
|
icon: "water-quality",
|
|
group: "monitoring",
|
|
initialVisible: true
|
|
},
|
|
{
|
|
id: "fx_20200423_pipes",
|
|
sourceLayer: SOURCE_LAYERS.fx_20200423_pipes,
|
|
geometry: "line",
|
|
available: true,
|
|
availability: "probe",
|
|
label: "GIS 管线",
|
|
icon: "source-pipe",
|
|
group: "source-gis",
|
|
initialVisible: false
|
|
},
|
|
{
|
|
id: "fx_20200811_dn100_pipes",
|
|
sourceLayer: SOURCE_LAYERS.fx_20200811_dn100_pipes,
|
|
geometry: "line",
|
|
available: true,
|
|
availability: "probe",
|
|
label: "DN100+ 台账",
|
|
icon: "source-pipe",
|
|
group: "source-gis",
|
|
initialVisible: false
|
|
},
|
|
{
|
|
id: "fx_20200423_valves",
|
|
sourceLayer: SOURCE_LAYERS.fx_20200423_valves,
|
|
geometry: "point",
|
|
available: true,
|
|
availability: "probe",
|
|
label: "GIS 阀门",
|
|
icon: "source-valve",
|
|
group: "source-gis",
|
|
initialVisible: false
|
|
},
|
|
{
|
|
id: "fx_20200423_drain_valves",
|
|
sourceLayer: SOURCE_LAYERS.fx_20200423_drain_valves,
|
|
geometry: "point",
|
|
available: true,
|
|
availability: "probe",
|
|
label: "排水阀",
|
|
icon: "source-valve",
|
|
group: "source-gis",
|
|
initialVisible: false
|
|
},
|
|
{
|
|
id: "fx_20200423_flow_meters",
|
|
sourceLayer: SOURCE_LAYERS.fx_20200423_flow_meters,
|
|
geometry: "point",
|
|
available: true,
|
|
availability: "probe",
|
|
label: "流量仪",
|
|
icon: "source-meter",
|
|
group: "source-gis",
|
|
initialVisible: false
|
|
},
|
|
{
|
|
id: "fx_20200423_hydrants",
|
|
sourceLayer: SOURCE_LAYERS.fx_20200423_hydrants,
|
|
geometry: "point",
|
|
available: true,
|
|
availability: "probe",
|
|
label: "消火栓",
|
|
icon: "source-hydrant",
|
|
group: "source-gis",
|
|
initialVisible: false
|
|
},
|
|
{
|
|
id: "fx_20200423_service_connections",
|
|
sourceLayer: SOURCE_LAYERS.fx_20200423_service_connections,
|
|
geometry: "point",
|
|
available: true,
|
|
availability: "probe",
|
|
label: "接水点",
|
|
icon: "source-connection",
|
|
group: "source-gis",
|
|
initialVisible: false
|
|
},
|
|
{
|
|
id: "fx_20200423_customer_meters",
|
|
sourceLayer: SOURCE_LAYERS.fx_20200423_customer_meters,
|
|
geometry: "point",
|
|
available: true,
|
|
availability: "probe",
|
|
label: "用户水表",
|
|
icon: "source-meter",
|
|
group: "source-gis",
|
|
initialVisible: false
|
|
},
|
|
{
|
|
id: "fx_20200423_non_residential_meters",
|
|
sourceLayer: SOURCE_LAYERS.fx_20200423_non_residential_meters,
|
|
geometry: "point",
|
|
available: true,
|
|
availability: "probe",
|
|
label: "非居民水表",
|
|
icon: "source-meter",
|
|
group: "source-gis",
|
|
initialVisible: false
|
|
}
|
|
] as const satisfies readonly SupplyLayerCatalogItem[];
|
|
|
|
export const SOURCE_GIS_SOURCE_IDS = SUPPLY_LAYER_CATALOG.filter(
|
|
(layer) => layer.group === "source-gis"
|
|
).map((layer) => layer.id) as WaterNetworkSourceId[];
|
|
|
|
export function isSourceGisSourceId(value: string): value is WaterNetworkSourceId {
|
|
return (SOURCE_GIS_SOURCE_IDS as readonly string[]).includes(value);
|
|
}
|
|
|
|
export const AVAILABLE_WATER_NETWORK_SOURCE_IDS = SUPPLY_LAYER_CATALOG.filter(
|
|
(layer) => layer.available
|
|
).map((layer) => layer.id) as WaterNetworkSourceId[];
|
|
|
|
export type AvailableWaterNetworkSourceId = (typeof AVAILABLE_WATER_NETWORK_SOURCE_IDS)[number];
|
|
export type GeoServerWaterNetworkSourceId = Exclude<AvailableWaterNetworkSourceId, "scada">;
|
|
|
|
export const GEOSERVER_WATER_NETWORK_SOURCE_IDS = AVAILABLE_WATER_NETWORK_SOURCE_IDS.filter(
|
|
(sourceId): sourceId is GeoServerWaterNetworkSourceId => sourceId !== "scada"
|
|
);
|
|
|
|
export function isAvailableWaterNetworkSourceId(
|
|
value: string
|
|
): value is AvailableWaterNetworkSourceId {
|
|
return (AVAILABLE_WATER_NETWORK_SOURCE_IDS as readonly string[]).includes(value);
|
|
}
|
|
|
|
export function createWaterNetworkSources(): Record<
|
|
GeoServerWaterNetworkSourceId,
|
|
VectorSourceSpecification
|
|
>;
|
|
export function createWaterNetworkSources(
|
|
sourceIds: readonly GeoServerWaterNetworkSourceId[],
|
|
workspace?: string
|
|
): Partial<Record<GeoServerWaterNetworkSourceId, VectorSourceSpecification>>;
|
|
export function createWaterNetworkSources(
|
|
sourceIds: readonly GeoServerWaterNetworkSourceId[] = GEOSERVER_WATER_NETWORK_SOURCE_IDS,
|
|
workspace = GEOSERVER_WORKSPACE
|
|
) {
|
|
return Object.fromEntries(
|
|
SUPPLY_LAYER_CATALOG.flatMap((layer) =>
|
|
layer.available &&
|
|
layer.sourceLayer !== undefined &&
|
|
sourceIds.includes(layer.id as GeoServerWaterNetworkSourceId)
|
|
? [[layer.id, createGeoServerVectorSource(layer.sourceLayer, workspace)] as const]
|
|
: []
|
|
)
|
|
) as Partial<Record<GeoServerWaterNetworkSourceId, VectorSourceSpecification>>;
|
|
}
|
|
|
|
function createGeoServerVectorSource(
|
|
sourceLayer: (typeof SOURCE_LAYERS)[keyof typeof SOURCE_LAYERS],
|
|
workspace: string
|
|
): VectorSourceSpecification {
|
|
return {
|
|
type: "vector",
|
|
promoteId: "id",
|
|
tiles: [
|
|
`${MAP_URL}/gwc/service/wmts/rest/${workspace}:${sourceLayer}/WebMercatorQuad/{z}/{y}/{x}?format=application/vnd.mapbox-vector-tile`
|
|
],
|
|
minzoom: 0,
|
|
maxzoom: 24
|
|
};
|
|
}
|
|
|
|
function createTiandituTileUrl(layer: "vec" | "cva" | "img" | "cia", token: string) {
|
|
return `https://t0.tianditu.gov.cn/${layer}_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=${layer}&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${token}`;
|
|
}
|
|
|
|
export function createBaseStyle(mapboxToken?: string, tiandituToken?: string): StyleSpecification {
|
|
const sources: StyleSpecification["sources"] = {
|
|
[SCADA_SOURCE_ID]: {
|
|
type: "geojson",
|
|
promoteId: "device_id",
|
|
data: { type: "FeatureCollection", features: [] }
|
|
}
|
|
};
|
|
const layers: StyleSpecification["layers"] = [
|
|
{
|
|
id: "background",
|
|
type: "background",
|
|
paint: {
|
|
"background-color": MAP_STYLE_TOKENS.canvas.primary
|
|
}
|
|
}
|
|
];
|
|
|
|
if (mapboxToken) {
|
|
sources["mapbox-light"] = {
|
|
type: "raster",
|
|
tiles: [
|
|
`https://api.mapbox.com/styles/v1/mapbox/light-v11/tiles/512/{z}/{x}/{y}@2x?access_token=${mapboxToken}`
|
|
],
|
|
tileSize: 512,
|
|
attribution:
|
|
'© <a href="https://www.mapbox.com/about/maps/">Mapbox</a> © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
|
};
|
|
sources["mapbox-satellite"] = {
|
|
type: "raster",
|
|
tiles: [
|
|
`https://api.mapbox.com/styles/v1/mapbox/satellite-streets-v12/tiles/512/{z}/{x}/{y}@2x?access_token=${mapboxToken}`
|
|
],
|
|
tileSize: 512,
|
|
attribution:
|
|
'© <a href="https://www.mapbox.com/about/maps/">Mapbox</a> © <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
|
};
|
|
|
|
layers.push({
|
|
id: "mapbox-light",
|
|
type: "raster",
|
|
source: "mapbox-light",
|
|
paint: {
|
|
"raster-opacity": 0.82,
|
|
"raster-saturation": -0.22,
|
|
"raster-contrast": 0.04
|
|
}
|
|
});
|
|
layers.push({
|
|
id: "mapbox-satellite",
|
|
type: "raster",
|
|
source: "mapbox-satellite",
|
|
layout: {
|
|
visibility: "none"
|
|
},
|
|
paint: {
|
|
"raster-opacity": 0.86,
|
|
"raster-saturation": -0.3,
|
|
"raster-contrast": 0.1
|
|
}
|
|
});
|
|
}
|
|
|
|
if (tiandituToken) {
|
|
const tiandituSources = [
|
|
["tianditu-vector", "vec"],
|
|
["tianditu-vector-annotation", "cva"],
|
|
["tianditu-image", "img"],
|
|
["tianditu-image-annotation", "cia"]
|
|
] as const;
|
|
|
|
tiandituSources.forEach(([sourceId, layer]) => {
|
|
sources[sourceId] = {
|
|
type: "raster",
|
|
tiles: [createTiandituTileUrl(layer, tiandituToken)],
|
|
tileSize: 256,
|
|
maxzoom: 18,
|
|
attribution: '© <a href="https://www.tianditu.gov.cn/">天地图</a>'
|
|
};
|
|
});
|
|
|
|
const vectorVisible = mapboxToken ? "none" : "visible";
|
|
layers.push(
|
|
{
|
|
id: "tianditu-vector",
|
|
type: "raster",
|
|
source: "tianditu-vector",
|
|
layout: { visibility: vectorVisible },
|
|
paint: { "raster-opacity": 0.9 }
|
|
},
|
|
{
|
|
id: "tianditu-vector-annotation",
|
|
type: "raster",
|
|
source: "tianditu-vector-annotation",
|
|
layout: { visibility: vectorVisible }
|
|
},
|
|
{
|
|
id: "tianditu-image",
|
|
type: "raster",
|
|
source: "tianditu-image",
|
|
layout: { visibility: "none" },
|
|
paint: {
|
|
"raster-opacity": 0.9,
|
|
"raster-saturation": -0.12,
|
|
"raster-contrast": 0.06
|
|
}
|
|
},
|
|
{
|
|
id: "tianditu-image-annotation",
|
|
type: "raster",
|
|
source: "tianditu-image-annotation",
|
|
layout: { visibility: "none" }
|
|
}
|
|
);
|
|
}
|
|
|
|
return {
|
|
version: 8,
|
|
glyphs: "https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf",
|
|
sources,
|
|
layers
|
|
};
|
|
}
|