feat: migrate workbench to React

This commit is contained in:
2026-08-26 15:01:07 +08:00
parent f15e69c868
commit 3ddb33e321
48 changed files with 16748 additions and 1509 deletions

View File

@@ -0,0 +1,160 @@
import Feature from 'ol/Feature';
import GeoJSON from 'ol/format/GeoJSON';
import LineString from 'ol/geom/LineString';
import Point from 'ol/geom/Point';
import VectorLayer from 'ol/layer/Vector';
import VectorSource from 'ol/source/Vector';
import CircleStyle from 'ol/style/Circle';
import Fill from 'ol/style/Fill';
import RegularShape from 'ol/style/RegularShape';
import Stroke from 'ol/style/Stroke';
import Style from 'ol/style/Style';
import type { Road, WorkbenchState } from '../types/state';
const geojson = new GeoJSON();
const source = () => new VectorSource();
export type LayerName =
| 'reference'
| 'gaodeReference'
| 'native'
| 'sidewalks'
| 'osm'
| 'lanes'
| 'edgeLines'
| 'directionArrows'
| 'markings'
| 'centerLines'
| 'controls'
| 'signals'
| 'connectors'
| 'diagnostics'
| 'selectedRoad';
export function createLayers(onRoad: () => Road | null, scene: () => boolean) {
const simple = (color: string, width = 1) =>
new Style({ fill: new Fill({ color: `${color}55` }), stroke: new Stroke({ color, width }) });
const layers: Record<LayerName, VectorLayer<VectorSource>> = {
reference: new VectorLayer({ source: source(), visible: false, style: simple('#8999a0') }),
gaodeReference: new VectorLayer({ source: source(), style: simple('#2563eb', 1.5) }),
native: new VectorLayer({
source: source(),
style: () => (scene() ? new Style({ fill: new Fill({ color: '#3f4b50' }) }) : simple('#296956')),
}),
sidewalks: new VectorLayer({
source: source(),
style: () => (scene() ? new Style({ fill: new Fill({ color: '#b7b9ad' }) }) : simple('#9b7c40')),
}),
osm: new VectorLayer({
source: source(),
style: (f) =>
new Style({
stroke: new Stroke({
color: f.get('road_id') === onRoad()?.id ? '#006e91' : '#263630',
width: f.get('road_id') === onRoad()?.id ? 5 : 2,
}),
}),
}),
lanes: new VectorLayer({
source: source(),
style: new Style({ stroke: new Stroke({ color: '#f5f6ee', width: 1.5, lineDash: [5, 4] }) }),
}),
edgeLines: new VectorLayer({
source: source(),
visible: false,
style: new Style({ stroke: new Stroke({ color: '#f5f6ee', width: 1 }) }),
}),
directionArrows: new VectorLayer({
source: source(),
style: new Style({ fill: new Fill({ color: '#f5f6ee' }), stroke: new Stroke({ color: '#d9dacf', width: 1 }) }),
}),
markings: new VectorLayer({
source: source(),
style: new Style({ fill: new Fill({ color: '#f5f6ee' }), stroke: new Stroke({ color: '#d9dacf', width: 1 }) }),
}),
centerLines: new VectorLayer({
source: source(),
style: new Style({ fill: new Fill({ color: '#f5be2a' }), stroke: new Stroke({ color: '#d29d16', width: 1 }) }),
}),
controls: new VectorLayer({
source: source(),
style: new Style({ fill: new Fill({ color: '#f5f6ee' }), stroke: new Stroke({ color: '#d9dacf', width: 1 }) }),
}),
signals: new VectorLayer({
source: source(),
style: new Style({
image: new RegularShape({
points: 4,
radius: 6,
angle: Math.PI / 4,
fill: new Fill({ color: '#263630' }),
stroke: new Stroke({ color: '#fff', width: 2 }),
}),
}),
}),
connectors: new VectorLayer({
source: source(),
style: new Style({ stroke: new Stroke({ color: '#ad3a76', width: 2, lineDash: [7, 5] }) }),
}),
diagnostics: new VectorLayer({
source: source(),
style: (f) =>
new Style({
image: new CircleStyle({
radius: 6,
fill: new Fill({ color: f.get('severity') === 'error' ? '#bf3b2e' : '#d49318' }),
stroke: new Stroke({ color: '#fff', width: 1 }),
}),
}),
}),
selectedRoad: new VectorLayer({
source: source(),
style: new Style({ stroke: new Stroke({ color: '#00a5cf', width: 8 }) }),
}),
};
return layers;
}
function features(value: unknown) {
return geojson.readFeatures((value || { type: 'FeatureCollection', features: [] }) as object, {
dataProjection: 'EPSG:4326',
featureProjection: 'EPSG:3857',
});
}
export function updateLayers(layers: ReturnType<typeof createLayers>, state: WorkbenchState, selected: Road | null) {
const get = (name: LayerName) => layers[name].getSource()!;
const put = (name: LayerName, value: unknown) => {
const target = get(name);
target.clear();
target.addFeatures(features(value));
};
put('reference', state.layers.osm2streetsRoadSurface);
put('gaodeReference', state.junctionReference?.converted);
put('native', state.layers.nativeRoadSurface);
get('native').addFeatures(features(state.layers.nativeIntersectionSurface));
put('sidewalks', state.layers.nativeSidewalkSurface);
const roads = state.compiled.model.roads.map(
(road) =>
new Feature({ geometry: new LineString(road.centerline).transform('EPSG:4326', 'EPSG:3857'), road_id: road.id }),
);
put('osm', { type: 'FeatureCollection', features: [] });
get('osm').addFeatures(roads);
put('lanes', state.layers.laneCenterlines);
put('edgeLines', state.layers.edgeLines);
put('directionArrows', state.layers.directionArrows);
put('markings', state.layers.laneSeparators);
get('markings').addFeatures(features(state.layers.turnArrows));
put('centerLines', state.layers.centerLines);
put('controls', state.layers.crosswalks);
get('controls').addFeatures(features(state.layers.vehicleStopLines));
put('signals', state.trafficSignals.assemblies);
put('connectors', state.layers.connectors);
put('diagnostics', {
type: 'FeatureCollection',
features: state.compiled.diagnostics
.filter((item) => item.geometry)
.map(({ geometry, ...properties }) => ({ type: 'Feature', properties, geometry: geometry! })),
});
put('selectedRoad', { type: 'FeatureCollection', features: [] });
if (selected)
get('selectedRoad').addFeature(
new Feature({ geometry: new LineString(selected.centerline).transform('EPSG:4326', 'EPSG:3857') }),
);
}