feat(sensor-placement): add scheme engineering editor

This commit is contained in:
2026-07-30 16:16:51 +08:00
parent 04557b9363
commit d643f09fcf
13 changed files with 2724 additions and 159 deletions
@@ -3,7 +3,7 @@ jest.mock("../MapComponent", () => ({
useMap: jest.fn(),
}));
jest.mock("../mapLifecycle", () => ({
markMapResourcePersistent: <T,>(resource: T) => resource,
markMapResourcePersistent: <T>(resource: T) => resource,
}));
jest.mock("ol/source/XYZ.js", () => ({
@@ -19,7 +19,9 @@ jest.mock("ol/layer/Tile.js", () => ({
constructor(options: any) {
this.source = options.source;
}
getSource() { return this.source; }
getSource() {
return this.source;
}
},
}));
jest.mock("ol/layer/Group", () => ({
@@ -29,14 +31,13 @@ jest.mock("ol/layer/Group", () => ({
constructor(options: any) {
this.layers = options.layers;
}
getLayers() { return { getArray: () => this.layers }; }
getLayers() {
return { getArray: () => this.layers };
}
},
}));
import {
createBaseLayerEntries,
createBaseLayerSources,
} from "./BaseLayers";
import { createBaseLayerEntries, createBaseLayerSources } from "./BaseLayers";
const getLeafSources = (layer: any): unknown[] => {
const childLayers = layer.getLayers?.().getArray?.();
@@ -47,6 +48,17 @@ const getLeafSources = (layer: any): unknown[] => {
};
describe("base layer resources", () => {
it("loads every tile source with anonymous CORS for canvas export", () => {
const sources = createBaseLayerSources();
Object.values(sources).forEach((source) => {
expect(
(source as unknown as { options: { crossOrigin?: string } }).options
.crossOrigin,
).toBe("anonymous");
});
});
it("creates independent layers backed by one shared source pool", () => {
const sources = createBaseLayerSources();
const primary = createBaseLayerEntries(sources);
@@ -33,6 +33,7 @@ const BASE_LAYER_METADATA = [
const createTileSource = (url: string, attributions: string) =>
new XYZ({
url,
crossOrigin: "anonymous",
tileSize: 512,
maxZoom: 20,
projection: "EPSG:3857",
@@ -57,24 +58,28 @@ export const createBaseLayerSources = () => ({
'数据来源:<a href="https://www.mapbox.com/">Mapbox</a> & <a href="https://www.openstreetmap.org/">OpenStreetMap</a>',
),
tiandituVector: new XYZ({
url: `https://t0.tianditu.gov.cn/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${TIANDITU_TOKEN}`,
projection: "EPSG:3857",
attributions: '数据来源:<a href="https://www.tianditu.gov.cn/">天地图</a>',
url: `https://t0.tianditu.gov.cn/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${TIANDITU_TOKEN}`,
crossOrigin: "anonymous",
projection: "EPSG:3857",
attributions: '数据来源:<a href="https://www.tianditu.gov.cn/">天地图</a>',
}),
tiandituVectorAnnotation: new XYZ({
url: `https://t0.tianditu.gov.cn/cva_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cva&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${TIANDITU_TOKEN}`,
projection: "EPSG:3857",
attributions: '数据来源:<a href="https://www.tianditu.gov.cn/">天地图</a>',
url: `https://t0.tianditu.gov.cn/cva_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cva&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${TIANDITU_TOKEN}`,
crossOrigin: "anonymous",
projection: "EPSG:3857",
attributions: '数据来源:<a href="https://www.tianditu.gov.cn/">天地图</a>',
}),
tiandituImage: new XYZ({
url: `https://t0.tianditu.gov.cn/img_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${TIANDITU_TOKEN}`,
projection: "EPSG:3857",
attributions: '数据来源:<a href="https://www.tianditu.gov.cn/">天地图</a>',
url: `https://t0.tianditu.gov.cn/img_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${TIANDITU_TOKEN}`,
crossOrigin: "anonymous",
projection: "EPSG:3857",
attributions: '数据来源:<a href="https://www.tianditu.gov.cn/">天地图</a>',
}),
tiandituImageAnnotation: new XYZ({
url: `https://t0.tianditu.gov.cn/cia_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cia&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${TIANDITU_TOKEN}`,
projection: "EPSG:3857",
attributions: '数据来源:<a href="https://www.tianditu.gov.cn/">天地图</a>',
url: `https://t0.tianditu.gov.cn/cia_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cia&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=${TIANDITU_TOKEN}`,
crossOrigin: "anonymous",
projection: "EPSG:3857",
attributions: '数据来源:<a href="https://www.tianditu.gov.cn/">天地图</a>',
}),
});
@@ -132,7 +137,9 @@ const BaseLayers: React.FC = () => {
return map ? [map] : [];
}, [data?.maps, map]);
const sharedSources = useMemo(() => createBaseLayerSources(), []);
const layerSetsRef = useRef(new WeakMap<OlMap, ReturnType<typeof createBaseLayerEntries>>());
const layerSetsRef = useRef(
new WeakMap<OlMap, ReturnType<typeof createBaseLayerEntries>>(),
);
const [isShow, setShow] = useState(false);
const [isExpanded, setExpanded] = useState(false);
const [activeId, setActiveId] = useState(INITIAL_LAYER);
@@ -244,7 +251,7 @@ const BaseLayers: React.FC = () => {
<div
className={clsx(
"absolute flex right-24 bottom-0 w-132 h-25 bg-white rounded-xl drop-shadow-xl shadow-black transition-all duration-300",
isShow ? "opacity-100" : "opacity-0"
isShow ? "opacity-100" : "opacity-0",
)}
onMouseEnter={handleEnter}
onMouseLeave={handleLeave}
@@ -265,7 +272,7 @@ const BaseLayers: React.FC = () => {
"object-cover object-left w-16 h-16 rounded-md border-2 border-white hover:ring-2 ring-blue-300",
{
"ring-1 ring-blue-300": activeId === item.id,
}
},
)}
/>
<span className="pt-1">{item.name}</span>