fix: refine workbench map layers

This commit is contained in:
2026-08-26 15:26:41 +08:00
parent 2f2bd6cbf0
commit 3c5f39ed73
6 changed files with 100 additions and 13 deletions

View File

@@ -0,0 +1 @@
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}

View File

@@ -0,0 +1 @@
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}

View File

@@ -0,0 +1,18 @@
# 优化工作台图层与场景标线
## Goal
移除不再需要的高德规整路口参考图层,提供可读的编译概览,并恢复场景模式下双向道路中心线的可辨识渲染。
## Confirmed Facts
- 科技园一路的三个双向路段在 `center_lines.geojson` 中都有完整的黄色虚线 feature不是编译缺失。
- React adapter 将原版中心线的属性驱动样式简化为固定样式,造成场景模式下辨识度不正确。
- 高德参考层仅在 React 前端图层 registry、图层开关和状态装载中使用不影响编译。
## Acceptance Criteria
- 工作台不显示或加载高德规整路口参考图层。
- 场景模式下保留中心线,并按线的颜色和虚/实线属性清晰渲染。
- 编译概览以中文分组指标和诊断摘要展示,不显示 schema、note、null 或 `[object Object]`
- 格式化、类型检查和生产构建通过。

View File

@@ -0,0 +1,26 @@
{
"id": "workbench-layer-scene-polish",
"name": "workbench-layer-scene-polish",
"title": "优化工作台图层与场景标线",
"description": "移除高德规整路口参考,优化编译概览,并修复场景模式下科技园一路中心线缺失",
"status": "in_progress",
"dev_type": null,
"scope": null,
"package": null,
"priority": "P2",
"creator": "dingkang",
"assignee": "dingkang",
"createdAt": "2026-08-26",
"completedAt": null,
"branch": null,
"base_branch": "main",
"worktree_path": null,
"commit": null,
"pr_url": null,
"subtasks": [],
"children": [],
"parent": null,
"relatedFiles": [],
"notes": "",
"meta": {}
}

View File

@@ -17,7 +17,6 @@ const layerLabels: Array<[LayerName, string]> = [
['edgeLines', '道路外缘线'], ['edgeLines', '道路外缘线'],
['controls', '斑马线与停止线'], ['controls', '斑马线与停止线'],
['signals', '红绿灯设施'], ['signals', '红绿灯设施'],
['gaodeReference', '高德规整路口参考'],
['reference', 'osm2streets 参考面'], ['reference', 'osm2streets 参考面'],
]; ];
const defaultLayers = Object.fromEntries( const defaultLayers = Object.fromEntries(
@@ -26,6 +25,50 @@ const defaultLayers = Object.fromEntries(
const direction = (road: Road) => (road.direction === 'forward' ? '沿 OSM 方向' : '逆 OSM 方向'); const direction = (road: Road) => (road.direction === 'forward' ? '沿 OSM 方向' : '逆 OSM 方向');
const label = (road: Road) => road.tags.name || `${road.highway}OSM ${road.osmWayIds.join(', ')}`; const label = (road: Road) => road.tags.name || `${road.highway}OSM ${road.osmWayIds.join(', ')}`;
const laneIndex = (id: string) => Number(id.split(':').at(-1)); const laneIndex = (id: string) => Number(id.split(':').at(-1));
const overviewMetrics = [
['nativeRoadCount', '方向道路'],
['nativeRoadSurfaceFeatures', '道路面'],
['nativeJunctionSurfaceFeatures', '路口面'],
['nativeLaneCenterlineFeatures', '车道中心线'],
['nativeCenterLineFeatures', '道路中心线'],
['nativeConnectorFeatures', '连接路径'],
['nativeCrosswalkFeatures', '斑马线'],
['nativeVehicleStopLineFeatures', '停止线'],
['unconnectedInteriorRoadEnds', '未连接端点'],
] as const;
function Overview({ comparison }: { comparison: Record<string, unknown> }) {
const severity = comparison.diagnosticsBySeverity as Record<string, number> | undefined;
return (
<section className="overview">
<div className="overview-grid">
{overviewMetrics.map(([key, label]) => (
<div key={key}>
<span>{label}</span>
<strong>{Number(comparison[key] || 0).toLocaleString('zh-CN')}</strong>
</div>
))}
</div>
<div className="overview-status">
<span>osm2streets </span>
<strong>
{comparison.osm2streetsAvailable === true
? `${Number(comparison.osm2streetsRoadSurfaceFeatures || 0)}`
: '未提供'}
</strong>
</div>
{severity && (
<div className="overview-diagnostics">
<span></span>
{Object.entries(severity).map(([level, count]) => (
<strong key={level}>
{level === 'error' ? '错误' : level === 'warning' ? '警告' : '提示'} {count}
</strong>
))}
</div>
)}
</section>
);
}
function App() { function App() {
const [state, setState] = useState<WorkbenchState>(); const [state, setState] = useState<WorkbenchState>();
const [selected, setSelected] = useState<Road | null>(null); const [selected, setSelected] = useState<Road | null>(null);
@@ -137,14 +180,7 @@ function App() {
))} ))}
<hr /> <hr />
<h1></h1> <h1></h1>
<dl> <Overview comparison={state.comparison} />
{Object.entries(state.comparison).map(([key, value]) => (
<>
<dt key={`${key}-t`}>{key}</dt>
<dd key={`${key}-d`}>{String(value)}</dd>
</>
))}
</dl>
<hr /> <hr />
<h1></h1> <h1></h1>
<div className="segmented"> <div className="segmented">

View File

@@ -15,7 +15,6 @@ const geojson = new GeoJSON();
const source = () => new VectorSource(); const source = () => new VectorSource();
export type LayerName = export type LayerName =
| 'reference' | 'reference'
| 'gaodeReference'
| 'native' | 'native'
| 'sidewalks' | 'sidewalks'
| 'osm' | 'osm'
@@ -34,7 +33,6 @@ export function createLayers(onRoad: () => Road | null, scene: () => boolean) {
new Style({ fill: new Fill({ color: `${color}55` }), stroke: new Stroke({ color, width }) }); new Style({ fill: new Fill({ color: `${color}55` }), stroke: new Stroke({ color, width }) });
const layers: Record<LayerName, VectorLayer<VectorSource>> = { const layers: Record<LayerName, VectorLayer<VectorSource>> = {
reference: new VectorLayer({ source: source(), visible: false, style: simple('#8999a0') }), reference: new VectorLayer({ source: source(), visible: false, style: simple('#8999a0') }),
gaodeReference: new VectorLayer({ source: source(), style: simple('#2563eb', 1.5) }),
native: new VectorLayer({ native: new VectorLayer({
source: source(), source: source(),
style: () => (scene() ? new Style({ fill: new Fill({ color: '#3f4b50' }) }) : simple('#296956')), style: () => (scene() ? new Style({ fill: new Fill({ color: '#3f4b50' }) }) : simple('#296956')),
@@ -72,7 +70,15 @@ export function createLayers(onRoad: () => Road | null, scene: () => boolean) {
}), }),
centerLines: new VectorLayer({ centerLines: new VectorLayer({
source: source(), source: source(),
style: new Style({ fill: new Fill({ color: '#f5be2a' }), stroke: new Stroke({ color: '#d29d16', width: 1 }) }), style: (feature) => {
const white = feature.get('color') === 'white';
const color = white ? '#faf9ee' : '#f5be2a';
const solid = feature.get('pattern') === 'solid';
return new Style({
fill: new Fill({ color }),
stroke: new Stroke({ color: solid ? color : white ? '#aeb0aa' : '#d29d16', width: solid ? 0.25 : 0.8 }),
});
},
}), }),
controls: new VectorLayer({ controls: new VectorLayer({
source: source(), source: source(),
@@ -126,7 +132,6 @@ export function updateLayers(layers: ReturnType<typeof createLayers>, state: Wor
target.addFeatures(features(value)); target.addFeatures(features(value));
}; };
put('reference', state.layers.osm2streetsRoadSurface); put('reference', state.layers.osm2streetsRoadSurface);
put('gaodeReference', state.junctionReference?.converted);
put('native', state.layers.nativeRoadSurface); put('native', state.layers.nativeRoadSurface);
get('native').addFeatures(features(state.layers.nativeIntersectionSurface)); get('native').addFeatures(features(state.layers.nativeIntersectionSurface));
put('sidewalks', state.layers.nativeSidewalkSurface); put('sidewalks', state.layers.nativeSidewalkSurface);