fix: persist and render native sidewalks
This commit is contained in:
@@ -6,6 +6,7 @@ const path = require("path");
|
||||
const OVERRIDE_SCHEMA = "native-road-overrides/v1";
|
||||
const MOTOR_HIGHWAYS = new Set(["motorway", "trunk", "primary", "secondary", "tertiary", "unclassified", "residential", "living_street", "service"]);
|
||||
const DEFAULT_WIDTHS = { motorway: 12, trunk: 10, primary: 10, secondary: 8, tertiary: 7, unclassified: 6, residential: 6, living_street: 5, service: 4 };
|
||||
const DEFAULT_SIDEWALK_WIDTH_METERS = 2;
|
||||
|
||||
function parseOsmRoads(xml) {
|
||||
const nodes = new Map();
|
||||
@@ -203,10 +204,37 @@ function compileGeometry(model, overrides = { overrides: [] }) {
|
||||
features.push({ type: "Feature", properties: { native_id: `surface:way/${wayKey}`, directional_road_ids: directions.map((item) => item.id).join(","), osm_way_ids: wayKey, width_m: totalWidth, lane_count: directions.reduce((sum, item) => sum + item.laneCount, 0), provenance: JSON.stringify(directions.map((item) => item.provenance)), override_ids: directions.flatMap((item) => item.appliedOverrideIds).join(",") }, geometry: { type: "Polygon", coordinates: [ring] } });
|
||||
}
|
||||
const lanes = compileLaneCenterlines(model, diagnostics);
|
||||
const sidewalks = compileSidewalkSurfaces(model, diagnostics);
|
||||
const connectorResult = compileConnectors(model, lanes, diagnostics, overrides);
|
||||
const junctionFeatures = compileJunctionSurfaces(model, lanes, connectorResult.features, connectorResult.movements, diagnostics);
|
||||
validateConnectorContainment(connectorResult.features, junctionFeatures, diagnostics);
|
||||
return { roadSurface: { type: "FeatureCollection", features }, intersectionSurface: { type: "FeatureCollection", features: junctionFeatures }, laneCenterlines: { type: "FeatureCollection", features: lanes.features }, connectors: { type: "FeatureCollection", features: connectorResult.features }, movements: connectorResult.movements, diagnostics };
|
||||
return { roadSurface: { type: "FeatureCollection", features }, sidewalkSurface: { type: "FeatureCollection", features: sidewalks }, intersectionSurface: { type: "FeatureCollection", features: junctionFeatures }, laneCenterlines: { type: "FeatureCollection", features: lanes.features }, connectors: { type: "FeatureCollection", features: connectorResult.features }, movements: connectorResult.movements, diagnostics };
|
||||
}
|
||||
|
||||
function compileSidewalkSurfaces(model, diagnostics) {
|
||||
const features = [];
|
||||
const byWay = new Map();
|
||||
for (const road of model.roads) {
|
||||
const key = road.osmWayIds.join(",");
|
||||
if (!byWay.has(key)) byWay.set(key, []);
|
||||
byWay.get(key).push(road);
|
||||
}
|
||||
for (const [wayKey, directions] of byWay) {
|
||||
const forward = directions.find((road) => road.direction === "forward") || directions[0];
|
||||
const backward = directions.find((road) => road.id !== forward.id);
|
||||
const totalWidth = directions.reduce((sum, road) => sum + road.widthMeters, 0);
|
||||
const sides = [
|
||||
["left", forward.sidewalkLeft || Boolean(backward?.sidewalkRight)],
|
||||
["right", forward.sidewalkRight || Boolean(backward?.sidewalkLeft)],
|
||||
];
|
||||
for (const [side, enabled] of sides) {
|
||||
if (!enabled) continue;
|
||||
const ring = sidewalkRing(forward.centerline, totalWidth / 2, totalWidth / 2 + DEFAULT_SIDEWALK_WIDTH_METERS, side === "left" ? 1 : -1);
|
||||
if (!ring) { diagnostics.push(diagnostic("warning", forward.id, forward.osmWayIds, "invalid-sidewalk-surface", "无法为该道路生成连续人行道面。", forward.centerline[0])); continue; }
|
||||
features.push({ type: "Feature", properties: { native_id: `sidewalk:way/${wayKey}:${side}`, osm_way_ids: wayKey, side, width_m: DEFAULT_SIDEWALK_WIDTH_METERS, directional_road_ids: directions.map((road) => road.id).join(","), provenance: "native-road-sidewalk/v1", override_ids: directions.flatMap((road) => road.appliedOverrideIds).join(",") }, geometry: { type: "Polygon", coordinates: [ring] } });
|
||||
}
|
||||
}
|
||||
return features;
|
||||
}
|
||||
|
||||
function validateConnectorContainment(connectors, junctionFeatures, diagnostics) {
|
||||
@@ -483,6 +511,14 @@ function roadRing(line, width) {
|
||||
return ring.every((point) => point.every(Number.isFinite)) ? ring : null;
|
||||
}
|
||||
|
||||
function sidewalkRing(line, innerOffset, outerOffset, side) {
|
||||
const inner = offsetLine(line, innerOffset * side);
|
||||
const outer = offsetLine(line, outerOffset * side);
|
||||
if (!inner || !outer) return null;
|
||||
const ring = [...inner, ...outer.reverse(), inner[0]];
|
||||
return ring.every((point) => point.every(Number.isFinite)) ? ring : null;
|
||||
}
|
||||
|
||||
function project(point, origin) { const scale = 111320; return [(point[0] - origin[0]) * scale * Math.cos(origin[1] * Math.PI / 180), (point[1] - origin[1]) * scale]; }
|
||||
function unproject(point, origin) { const scale = 111320; return [point[0] / (scale * Math.cos(origin[1] * Math.PI / 180)) + origin[0], point[1] / scale + origin[1]]; }
|
||||
function diagnostic(severity, subjectId, sourceIds, rule, message, coordinate) { return { id: `diagnostic:${rule}:${subjectId}`, severity, subjectId, sourceIds, rule, message, geometry: coordinate ? { type: "Point", coordinates: coordinate } : null }; }
|
||||
|
||||
Reference in New Issue
Block a user