fix(510-1 자동 제안 후보): 간격 보충 기준에 지운 교차 후보도 넣음(검증_sub3 지운 385 옆 378 후보가 섬) · 옛 제안 목록의 관 자리 줄 걷음([자동 제안] 목록과 두 벌 겹침 · 지도 점선 원 = 자동 제안 후보만)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -245,7 +245,7 @@ export function createDrainagePanel(callbacks: DrainagePanelCallbacks = {}): Dra
|
||||
markedChainage,
|
||||
stationIntervalM: MAP_STATION_INTERVAL_M,
|
||||
intervalSpans,
|
||||
candidates: [...live.suggest.candidates(), ...propose.candidates()],
|
||||
candidates: propose.candidates(),
|
||||
});
|
||||
updateImageTransform();
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
/* =============================================================================
|
||||
* B05_Profile_UI_Drainage_Suggest.ts
|
||||
* 배수유역 칸 제안 목록(347-3) — 관경 · 시설 종류 · 관 자리 후보를 보여 주고 [적용] 한 번에 관경 · 시설만
|
||||
* 관 초안으로(관 자리 = [자동 제안] · 369).
|
||||
* 배수유역 칸 제안 목록(347-3) — 관경 · 시설 종류를 보여 주고 [적용] 한 번에 관 초안으로.
|
||||
* 모으기 · 얹기 규칙은 `logic/B05_Profile_Drainage_Suggest.ts` · 자동 바꿈 없음.
|
||||
* 관 자리 후보는 배수유역도에도 점선 원으로 그림(`candidates()` → 그리기 판).
|
||||
* 관 자리 후보 = [자동 제안] 목록(510 · `B05_Profile_UI_Drainage_Propose`) — 여기 안 올림(두 목록 겹침).
|
||||
* ========================================================================== */
|
||||
|
||||
import type {
|
||||
@@ -27,8 +26,7 @@ const facilityName = (id: string): string => FACILITY_NAME[id] ?? id;
|
||||
/** 제안 한 줄 글. */
|
||||
export function suggestionText(item: Suggestion): string {
|
||||
const at = `측점 ${Math.round(item.chainageM)} m`;
|
||||
if (item.kind === "position")
|
||||
return `${at} — 관 자리 후보(흐름이 모이는 오목 자리 · [자동 제안])`;
|
||||
if (item.kind === "position") return at; // 모으지 않음(510 · [자동 제안] 목록)
|
||||
if (item.kind === "facility")
|
||||
return `${at} — ${facilityName(item.from)} → ${facilityName(item.to)} 검토`;
|
||||
return `${at} — 관경 ${item.from === null ? "미정" : `Ø${item.from}`} → Ø${item.to}`;
|
||||
@@ -39,8 +37,6 @@ export interface DrainageSuggest {
|
||||
update(response: DetailBasinResponse): void;
|
||||
/** 노선 · 관이 바뀌어 낡음 — [적용] 끔(옛 노선 기준 제안이 새 노선에 안 들어가게). */
|
||||
setStale(stale: boolean): void;
|
||||
/** 관 자리 후보 누가거리 — 배수유역도 점선 원. */
|
||||
candidates(): number[];
|
||||
}
|
||||
|
||||
export function createDrainageSuggest(
|
||||
@@ -66,9 +62,9 @@ export function createDrainageSuggest(
|
||||
root.append(head, list);
|
||||
let suggestions: Suggestion[] = [];
|
||||
let stale = false;
|
||||
/** [적용] 켜짐 — 낡지 않고 관경 · 시설 제안이 있을 때(관 자리 후보만이면 할 일 없음). */
|
||||
/** [적용] 켜짐 — 낡지 않고 관경 · 시설 제안이 있을 때. */
|
||||
const paintApply = (): void => {
|
||||
apply.disabled = stale || !suggestions.some((item) => item.kind !== "position");
|
||||
apply.disabled = stale || suggestions.length === 0;
|
||||
apply.title = stale ? "노선이 바뀌어 제안이 낡음 — 새 계산 뒤 적용" : applyTitle;
|
||||
};
|
||||
|
||||
@@ -82,7 +78,7 @@ export function createDrainageSuggest(
|
||||
update(response) {
|
||||
suggestions = response.stale
|
||||
? []
|
||||
: collectSuggestions(response.pipe_points, response.basins, response.pipe_candidates ?? []);
|
||||
: collectSuggestions(response.pipe_points, response.basins, []);
|
||||
root.hidden = suggestions.length === 0;
|
||||
title.textContent = `제안 ${suggestions.length}건`;
|
||||
paintApply();
|
||||
@@ -98,7 +94,5 @@ export function createDrainageSuggest(
|
||||
stale = next;
|
||||
paintApply();
|
||||
},
|
||||
candidates: () =>
|
||||
suggestions.filter((item) => item.kind === "position").map((item) => item.chainageM),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -237,7 +237,7 @@ def propose_pipes(
|
||||
|
||||
상태 = held(라벨 짝 관 · 교차 5 m 안 관) · dismissed(지움 표지 짝 · 라벨 = 그 표지) · new.
|
||||
짝 잡기 = 노선 [저장](`add_crossing_pipes`)과 같은 `claim_crossings`(관 라벨 + 지움 표지 함께).
|
||||
간격 보충 = 지금 관 + 새 교차 후보 사이가 최대 간격을 넘는 구간(`suggest_pipe_positions`).
|
||||
간격 보충 = 지금 관 + 교차 후보(새 · 지운 관) 사이 최대 간격 넘는 구간(suggest_pipe_positions).
|
||||
"""
|
||||
from common_util.common_util_drainage_detail import suggest_pipe_positions
|
||||
from common_util.common_util_route_geometry import interpolate_vertex
|
||||
@@ -270,7 +270,7 @@ def propose_pipes(
|
||||
}
|
||||
)
|
||||
if strength_curve is not None:
|
||||
base = sorted({*taken, *(item["chainage_m"] for item in found if item["state"] == "new")})
|
||||
base = sorted({*taken, *(item["chainage_m"] for item in found if item["state"] != "held")})
|
||||
placed = [
|
||||
StructureCandidate(chainage_m=c, x=xy[0], y=xy[1], reason="confirmed")
|
||||
for c in base
|
||||
|
||||
@@ -60,8 +60,9 @@ def test_manual_update_answer_lands_after_same_rerun(r):
|
||||
|
||||
|
||||
def test_suggestions_and_apply(r):
|
||||
assert r["suggest"]["shown"] and r["suggest"]["title"] == "제안 2건"
|
||||
assert len(r["suggest"]["rows"]) == 2
|
||||
# 관 자리 후보(200)는 제안 목록에 안 올림 — [자동 제안] 목록 몫(510)
|
||||
assert r["suggest"]["shown"] and r["suggest"]["title"] == "제안 1건"
|
||||
assert len(r["suggest"]["rows"]) == 1
|
||||
points = {p["chainage_m"]: p for p in r["applied"]["points"]}
|
||||
assert points[100]["options"]["pipe_diameter_mm"] == 1200
|
||||
assert 200 not in points # 관 자리 후보는 [적용]이 안 더함 — [자동 제안] 몫(369)
|
||||
|
||||
@@ -82,9 +82,10 @@ def test_간격_보충_분석_있을_때만():
|
||||
found = propose_pipes(vertices, crossings, pipes, dismissed, curve)
|
||||
spacing = [f for f in found if f["kind"] == "spacing"]
|
||||
assert spacing and all(f["state"] == "new" and "stream_label" not in f for f in spacing)
|
||||
# 지금 관 + 새 교차 후보 사이 최대 간격(100 m) 넘는 구간만 · 320 뒤 280 m
|
||||
taken = [150.0, 252.0, 320.0]
|
||||
assert all(min(abs(f["chainage_m"] - t) for t in taken) > 0.5 for f in spacing)
|
||||
# 지금 관 + 교차 후보(새 · 지운 관) 사이 최대 간격(100 m) 넘는 구간만 · 320 뒤 280 m ·
|
||||
# 지운 교차(50) 곁에 간격 보충이 붙지 않음(화면 실측 = 지운 385 옆 378)
|
||||
taken = [50.0, 150.0, 252.0, 320.0]
|
||||
assert all(min(abs(f["chainage_m"] - t) for t in taken) > 5.0 for f in spacing)
|
||||
assert any(f["chainage_m"] > 320.0 for f in spacing)
|
||||
assert not [f for f in propose_pipes(vertices, crossings, pipes, dismissed) if f["kind"] == "spacing"]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user