feat(측점열쇠): 346-2 덧 2 열쇠 한 벌 — 구간형 :start · :end · 묶음 측점 종류(grid · end) · 같은 열쇠 둘째 규칙 공용
- common_util_station_key(ts · py 짝): role 덧붙임(구간형 시작 · 끝 = structure:<id>:start · :end · 기준은 structure:<id> 그대로) · grid(원래 격자 번호) · end(BP/EP) 종류 · dedupeStationKeys(같은 열쇠 둘째부터 at:<누가거리>) - B06 stationKeysOf 가 같은 규칙(측점 계획 · 구조물 목록의 시작 · 끝 자리 · 공용 dedupe) - 거울 시험에 새 갈래(구간형 · grid · end · dedupe) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QfHKQs7J3hQELKMWQMUJEy
This commit is contained in:
@@ -18,7 +18,13 @@
|
||||
import { readState, writeState } from "../A00_Common/b_page_state";
|
||||
import { pipeIdAt } from "../A00_Common/design/design_pipe_ids";
|
||||
import { showToast } from "@ui/ui_template_elements";
|
||||
import { chainageKey, looseChainage, stationKey } from "@util/common_util_station_key";
|
||||
import {
|
||||
chainageKey,
|
||||
dedupeStationKeys,
|
||||
looseChainage,
|
||||
stationKey,
|
||||
type StationKeyRole,
|
||||
} from "@util/common_util_station_key";
|
||||
import { inferStationInterval } from "@util/common_util_svg";
|
||||
import type { CrossSection, SectionDetailResponse } from "./B06_Section_Api_Fetch";
|
||||
|
||||
@@ -46,7 +52,8 @@ type Raw = Record<string, unknown> & { [HINT]?: Record<string, number> };
|
||||
|
||||
const lists = new Map<string, Keyed[]>();
|
||||
/** 프로젝트마다 구조물 측점 → id(측점 계획 · 구조물 목록). */
|
||||
const owners = new Map<string, Array<{ id: string; kind: "pipe" | "structure"; at: number }>>();
|
||||
type Owner = { id: string; kind: "pipe" | "structure"; at: number; role: StationKeyRole };
|
||||
const owners = new Map<string, Owner[]>();
|
||||
let current: { projectId: string; routeId: number } | null = null;
|
||||
|
||||
const scopeOf = (projectId: string, routeId: number): string => `${projectId}:${routeId}`;
|
||||
@@ -54,7 +61,7 @@ const scopeOf = (projectId: string, routeId: number): string => `${projectId}:${
|
||||
/** 측점 계획(초안 · `planStations`)이 낸 덮는 구조물 id — 관 세션 id 는 `pipe-` 머리. */
|
||||
export function useStationOwners(
|
||||
projectId: string,
|
||||
planned: ReadonlyArray<{ chainage_m: number; owners: string[] }>,
|
||||
planned: ReadonlyArray<{ chainage_m: number; owners: string[]; role?: StationKeyRole }>,
|
||||
): void {
|
||||
owners.set(
|
||||
projectId,
|
||||
@@ -63,6 +70,7 @@ export function useStationOwners(
|
||||
id,
|
||||
kind: id.startsWith("pipe-") ? ("pipe" as const) : ("structure" as const),
|
||||
at: station.chainage_m,
|
||||
role: station.role ?? "anchor",
|
||||
})),
|
||||
),
|
||||
);
|
||||
@@ -71,12 +79,25 @@ export function useStationOwners(
|
||||
/** 구조물 목록(횡단 보기) — 한 자리 구조물의 id. 관 자리는 관 장부가 정함. */
|
||||
export function useStructureIds(
|
||||
projectId: string,
|
||||
structures: ReadonlyArray<{ structure_id?: string | null; chainage_m?: number | null }>,
|
||||
structures: ReadonlyArray<{
|
||||
structure_id?: string | null;
|
||||
chainage_m?: number | null;
|
||||
start_m?: number | null;
|
||||
end_m?: number | null;
|
||||
}>,
|
||||
): void {
|
||||
const known = (owners.get(projectId) ?? []).filter((owner) => owner.kind === "pipe");
|
||||
for (const item of structures) {
|
||||
if (item.structure_id && typeof item.chainage_m === "number")
|
||||
known.push({ id: item.structure_id, kind: "structure", at: item.chainage_m });
|
||||
const id = item.structure_id;
|
||||
if (!id) continue;
|
||||
// 기준 측점 = structure:<id> · 구간형 시작 · 끝 = :start · :end(열쇠 한 벌 · common_util_station_key)
|
||||
const marks: Array<[StationKeyRole, number | null | undefined]> = [
|
||||
["anchor", item.chainage_m],
|
||||
["start", item.start_m],
|
||||
["end", item.end_m],
|
||||
];
|
||||
for (const [role, at] of marks)
|
||||
if (typeof at === "number") known.push({ id, kind: "structure", at, role });
|
||||
}
|
||||
owners.set(projectId, known);
|
||||
}
|
||||
@@ -84,20 +105,19 @@ export function useStructureIds(
|
||||
const looksLikePipe = (section: CrossSection): boolean =>
|
||||
Boolean(section.culvert || section.box) || /^.+ D\d+/.test(section.structure ?? "");
|
||||
|
||||
/** 측점 목록 → 열쇠(같은 열쇠가 둘이면 뒤엣것은 `at:`). */
|
||||
/** 측점 목록 → 열쇠(같은 열쇠가 둘이면 뒤엣것은 `at:` · `dedupeStationKeys`). */
|
||||
export function stationKeysOf(
|
||||
projectId: string | null,
|
||||
sections: ReadonlyArray<CrossSection>,
|
||||
intervalM: number | null,
|
||||
): Keyed[] {
|
||||
const known = projectId ? (owners.get(projectId) ?? []) : [];
|
||||
const seen = new Set<string>();
|
||||
return sections.map((section) => {
|
||||
const keys = sections.map((section) => {
|
||||
const at = section.chainage_m;
|
||||
let key: string;
|
||||
if (section.kind === "irregular") {
|
||||
const owner = known.find((entry) => Math.abs(entry.at - at) < NEAR_M);
|
||||
if (owner) key = stationKey("irregular", at, intervalM, owner.id, owner.kind);
|
||||
if (owner) key = stationKey("irregular", at, intervalM, owner.id, owner.kind, owner.role);
|
||||
else if (looksLikePipe(section))
|
||||
key = stationKey("pipe", at, intervalM, pipeIdAt(at, projectId));
|
||||
else
|
||||
@@ -105,10 +125,10 @@ export function stationKeysOf(
|
||||
? stationKey("regular", at, intervalM)
|
||||
: stationKey("irregular", at, intervalM);
|
||||
} else key = stationKey(section.kind, at, intervalM);
|
||||
if (seen.has(key)) key = `at:${chainageKey(at)}`;
|
||||
seen.add(key);
|
||||
return { key, chainageM: at };
|
||||
return key;
|
||||
});
|
||||
const chainages = sections.map((section) => section.chainage_m);
|
||||
return dedupeStationKeys(keys, chainages).map((key, i) => ({ key, chainageM: chainages[i] }));
|
||||
}
|
||||
|
||||
type KeyedDetail = Pick<SectionDetailResponse, "cross_sections" | "longitudinal">;
|
||||
|
||||
Reference in New Issue
Block a user