Files
Aislo/docs/raw/plans/2026-09-03_plan_completed_items.md
T
eomsangdonandClaude Opus 5 eb30b774f8 chore(docs): docs 폴더 git 추적 전환 · PLAN·OWNERS 최상위 이관
- `docs/` 를 .gitignore 에서 빼 git 추적 대상으로 전환 (위키·완료 이력·검증 기록)
- `docs/raw/PLAN.md` · `docs/raw/OWNERS.md` 를 저장소 최상위로 이동 후 .gitignore 에 등록
  — 창끼리 공유하되 저장소에는 안 올리는 장부
- 살아 있는 경로 참조 7개 파일 정정 (아카이브 107건은 그때 사실이라 그대로 둠)
- graphify 날짜별 산출물(`docs/wiki/graphify-out/20*/`) 제외 — `graphify update` 가 다시 만듦

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 18:19:07 +09:00

425 lines
35 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 2026-09-03 완료 항목 이관
> 사용자가 완료 항목의 위치 정리를 요청했으며, 요청에 따라 위키 관리자의 2차 교차검증과 별도 검증보고서는 생략했다.
> `docs/raw/PLAN.md`에서 완료 체크 항목과 그 하위 원인·조치·자체검증 설명을 이관했다.
### 2026-09-03 화면 개선 (메인 창 · B05·B06 담당)
- [X] **I형 집수정 배관이 횡단도에 삼각형으로 그려짐** (사용자 항목 4).
- 원인 — `B06_Section_UI_Cross_Culvert_Solve.ts` `pipeAxisSolver.corners()` 가 관 끝단
두 꼭짓점을 마감면(I형 벽 안쪽 변) 선분 안으로 `clampToFace` 하는데, 원지반이 벽 상단
위라 관 시작점이 벽 상단에 붙으면 위·아래 교점이 **같은 끝점으로 접혀** 사각형이
삼각형이 됨. 13+4.1 실측 — 관 폴리곤 `[[594.6,418.7],[338.9,537.3],[348.5,505.4],[594.6,418.7]]`
(1번=4번 꼭짓점 동일), 집수정 벽 상단 `y=419` = 관 시작점 `y=418.7`.
- 조치 — 자른 끝단 길이가 관경보다 짧으면(비스듬히 자르면 항상 관경 이상) 접힌 것으로
보고 **축 직각 마감(fallback)** 으로 되돌림. 편집 단일 창구인 `corners()` 한 곳만 수정.
- 자체검증 — 공용 브라우저 B06(8+17.3·13+4.1 등 8개 단면) 실측:
수정 전 유입측 끝단 길이 `0.0 px`(세월교 1건 제외 전부), 수정 후 `18.7~58.4 px`
로 전부 살아남. `npm run typecheck` 통과, prettier 무변경.
- **2차 수정 (2026-09-03 사용자 지적 「측벽과 배관의 형상이 교차하고 있어야 함」)** —
1차 수정은 삼각형만 없앴을 뿐 관이 벽 위에 얹혀 벽과 만나지 않았음. ㄴ형과 대조해
(관이 바닥판 윗면에서 시작해 벽 안에 물림) 원인 확정: I형 관 하단이 벽면×원지반 교점인데
원지반이 벽 상단보다 높으면 교점이 상단으로 밀림. **교점을 벽 상단에서 관경+여유(0.5m)
아래로 제한**해 관이 벽을 관통하게 함. 벽을 키우는 안은 벽 기울기(1:0.3)로 base 가
계류측으로 밀려 벽이 7.5m 까지 자라 폐기.
- 2차 자체검증 — I형 8개 단면 관·벽 세로 겹침 `18.1~56.4 px`(수정 전 0). ㄴ형 대조는
13+4.1 을 조정창에서 ㄴ형으로 바꿔 확인 후 세션값 원복. pytest 370 passed·17 skipped.
- 육안검증 기록(아티팩트): https://claude.ai/code/artifact/c4ef706c-c3d8-461c-ae54-6c376980c043
- [X] **유토곡선 세부 항목이 리로딩 중 보였다가 사라짐** (사용자 항목 3) — 원인 규명 완료.
- 재현(공용 브라우저 30ms 폴링) — 2.0초 그림: 절토 6,908 / 성토 42,050 / 운반 5,571㎥,
평형선 6·장비 띠 9·balloon 12. 3.5초 그림: 절토 3,704 / 성토 14,881 / 운반 0㎥,
평형선 1·띠 0·balloon 1.
- 원인 — `B06_Section_UI_Page.ts:285 reconcileStaleDesigns()` 가 저장분 횡단 설계 중
현재 종단 계획선과 어긋난 측점을 일괄 재계산해 제자리 교체함. 첫 그림은 **재계산 전
저장분**(20m 구간당 성토 860㎥ — 임도에 불가능한 값), 둘째가 정본. 캐시 덮어쓰기·
숨김·토글 문제 아님(범례는 두 시점 모두 「토량 분배」 켜짐).
- 벌룬이 정본 상태에서 안 생기는 이유 — 성토 14,881 vs 절토 3,704(4배)라 누가토량 곡선이
0선을 한 번도 넘지 않아 평형선 계단이 서지 않음. 극값 가지치기 기준(범위×2%) 탓이
아님 — 실무 6건은 같은 기준으로도 극값이 14~53개 남음.
- 실무 원본 대조(`유토곡선.MASS` 6건 파싱) — 최종 누가토량 **+49 ~ +324㎥**, 0선 교차
3~17회. 용화는 **11,094㎥ · 교차 0회**. 실무 `EARTH.DAT` 헤더 6건 전부 무대 20.0 /
도자 **60.0** m (현행 구현은 20/70).
- 조치 — 사용자 확정(2026-09-03 「절·성토 불균형 판정은 사용자 몫, 자동 조정 금지」)에
따라 **진단만 노출**: 유토곡선 요약줄에 「최종 누가토량」 칩 신설(툴팁에 균형 의미와
평형선 미생성 사유). B05·B06 공용 함수라 양쪽에 함께 뜸.
- 지식DB — [유토곡선_토량배분](../../resources/knowledge/technical_info/01_임도/03_계산정보/유토곡선_토량배분.md)
신설(실무 6건 관측표·장비 한계거리 후보·근거 공백 명시), 미결 20·21 등재.
- 자체검증 — 공용 브라우저 실측: 요약 칩이
`절토(자연) 3,704.6㎥ · 성토 14,881.8㎥ · 토취 11,094.6㎥ · 최종 누가토량 −11,094.6㎥ · 운반토량 계 0㎥ · 운반·성토 검산 −3,787㎥` 로 표시됨. `npm run typecheck` 통과,
prettier 통과.
- [X] **옛 계획고 유토곡선 노출 차단 + 사면 미교차 경고** (2026-09-03 사용자 확정).
- 「새 값만 보여주기」 — 저장 횡단이 지금 계획선과 어긋나면(재계산 예약) 유토곡선을
그리지 않고 안내만 띄움. B05·B06 양쪽 적용(`staleDesignChainages` 공용 판정).
- 「사면 미교차는 경고로 대체」 — 사면이 계산 반폭(±20m) 끝까지 원지반과 안 만나 면적이
잘린 측점에 카드 경고. 판정은 면적을 내는 엔진 한 곳(`B06_Section_Engine_Design`)이
`slope_unclosed` 로 내림 — 화면·수량·도면이 같은 기준을 봄. 계산은 무수정.
- 실측 근거 — 용화 65측점 중 **56개가 낡음**, 계획고 차 9.36~+5.83m(예: 0+20 저장
875.36 → 현재 870.26, 저장분 성토 단면 85.9㎡ / 43+0 지점 256.4㎡).
- 자체검증 — 공용 브라우저: 2.6초 「재계산 중」 안내만(옛 값 미노출), 4.4초 정본 표시,
사면 미교차 경고 13/65측점. `pytest 366 passed·17 skipped`, typecheck·prettier 통과.
- [X] **B05·B06 유토곡선 일원화** (2026-09-03 사용자 지시 「하나의 데이터를 두 페이지에
보여 주는 기능이니 로직·템플릿이 다르면 일원화」). 갈림 3곳을 모두 한 벌로.
- **보는 노선** — B05는 최신 경로(`get_latest_route`), B06은 최신 **확정** 경로만 열어
노선을 다시 탐색한 프로젝트에서 서로 다른 노선을 봤음. 실측: B05 route 126(DRAFT) /
B06 route 125(CONFIRMED), 같은 20m 측점 계획고 870.26 ↔ 875.36 · 성토 4.82㎡ ↔ 85.9㎡.
`get_workflow_route_context()`(최신 경로) 신설, B06 화면 context가 사용.
납품 도면(B07)이 쓰는 확정 전용 창구는 그대로 둠.
- **횡단 재계산 창구** — 호출이 두 벌이라 인자가 갈림(B06만 표준 단면값·암 경계 오프셋
전달, 보존 사용자값 2개 ↔ 7개). → `B06_Section_Cross_Refresh.refreshCrossDesigns()`
하나로 통합, 세션 편집값은 저장소에서 직접 읽어 패널 없는 B05도 같은 값을 보냄.
- **낡음 판정** — 「옛 암 2단계 필드 누락」 조건이 B06 페이지에만 있어 B05는 재계산을
건너뜀(실측 절토 합 228.52㎡ ↔ 270.12㎡). → 공용 `staleDesignChainages()` 안으로 이관.
- 자체검증 — 공용 브라우저 실측: 두 화면 모두 route 126, 요약줄 문자열 **완전 일치**
(`절토(자연) 4,526.8㎥ · 성토 14,881.8㎥ · 토취 10,213.3㎥ · 최종 누가토량 −10,213.3㎥ · 운반토량 계 0㎥ · 운반·성토 검산 −4,669㎥`). `pytest 370 passed·17 skipped`
(일원화 소스검사 4건 신설 `tmp/tests/test_masshaul_single_source.py`, 기존 stale 규칙
테스트 1건은 조건 이관에 맞춰 갱신), typecheck·prettier·ruff 통과.
### 초기 계산 실패 처리 후속
- [X] `initial_design.failed` 마커 — **백필·재확정 적용 둘 다 안 함**(2026-09-03 사용자 결정).
새 실패만 마커를 남기는 현행 유지.
### LAS 없는 설계 — 곡선 경고 판정 후속
- [X] 지표면 종류별 계곡 시설 차이 — **원인 조사 완료**(2026-09-03).
- 시설 종류는 유효직경 하나로 갈린다: 1,500mm 초과 = BOX암거, 2,000mm 초과 = 세월교,
그 이하는 규격 스냅(800·1000·1200·1500). 임계 유량 실측 — BOX `3.711 ㎥/s`,
D800 `1.056 ㎥/s`.
- 저장 산출물 대조(같은 노선, 두 번의 LAS 해석): **200.9m 유역이 임계 위에 아슬아슬**하다 —
면적 28,907㎡ → D `1,529.3mm`, 29,209㎡ → D `1,537.3mm`. 임계 1,500mm 를 **2%** 차이로
넘는다. 면적이 1~2%만 작아지면 그대로 관(D1500)으로 내려간다. 같은 LAS 해석끼리도
면적이 1.0% 흔들린다(격자·트림 차이).
- 반면 관측된 **BOX → 관D800** 은 그 흔들림으로 설명되지 않는다. D800 이 되려면 설계유량이
3.5배(면적 기준 약 1/3.5) 줄어야 한다 — 표고 해상도 차이가 아니라 **유역 분할 자체가
달라진 것**이다. 같은 관측에서 4번째 시설 자리가 275.71m → 297.71m 로 옮겨진 것이 그
신호(관 위치가 바뀌면 유역 귀속이 통째로 재배분된다).
- 149.7m 유역은 414,106㎡ · D `4,719mm` 로 세월교 판정 — 관측과 일치.
### B08 CAD 후속
- [X] 도각 편집 안내 띠 — **이미 해결돼 있음**(2026-09-02 사이드바 액션 1행으로 이동).
2026-09-03 화면 실측: 띠는 `.b07-drawing-actions` 안(사이드바 하단 y=1243)에 평소 숨김
상태로 있어 CAD 리본과 겹칠 수 없음. 코드 변경 없음.
- [X] **SVG 서명 — CAD 벡터로 변환**(2026-09-03 사용자 결정). `_Engine_Cad_Svg` 신설(path
M·L·H·V·C·Q·S·T·Z, 곡선 16분할, viewBox 비율 유지·y 뒤집기). 래스터는 종전대로 ImageEntity.
- **화면 검증 불가**(2026-09-03) — 회사 자산 폴더(`storage/1/assets/`)의 서명·로고가
**전부 PNG**라 SVG 경로를 탈 자산이 하나도 없다. 단위 검증
(`tmp/tests/test_b07_signature_vector.py` 5건)만 선 상태. 화면으로 보려면 SVG 서명을
한 장 올려야 하므로 **사용자 판단 대기**(시험용 SVG 업로드 여부).
### B08 횡단도·장 배치 후속
- [x] **장 단위 확정 흐름 실측**(2026-09-03) — 용화 검증본 `d6997dd9` 3장(`cross_s00220m`)을
실제로 확정해 결함 3건을 찾아 고침(커밋 `4fdbcaf0`).
- ① 수량표 역추출이 측점을 구분하지 않음 — `_table_values_from_cells()` 가 도면의 표를
전부 훑어 **마지막 값**을 모든 측점에 기록(실측: 4측점 전부 지반고 `837.21`, 도면에는
840.87/840.33/836.45/837.21). 표 엔티티 id `uuid5(_ENTITY_NS, f"{drawing_id}:qtable")`
로 그 측점 표만 읽고, 못 찾으면 옛 도면 폴백.
- ② 계획고가 통째로 빔 — 장 배치 입력 원본에 계획고가 없어 21항목 중 지반고 1개만 채워짐.
`_quantity_table(source, design)` 이 횡단 설계의 `design_elevation_m` 도 보게 함.
- ③ 확정 해제(invalidate) 404 — 도면 목록을 `designs` 없이 만들어 장 나눔이 달라짐.
목록·확정과 같은 인자를 쓰게 함.
- 자체검증(백엔드 재시작 후 실동작) — invalidate `404 → 200`, 표 값이 측점마다 다름
(`ground=840.87 planned=840.87 fill=0.00` · `ground=831.72 planned=836.64 fill=4.92`),
`manifest.json.quantity_tables` 5측점(220·240·260·264·280) 각각 4/21 항목 채움
(나머지 17칸은 CAD 에서 사용자가 채우는 자리). 시험 확정은 전부 해제해 원상복구
(확정 남은 도면 `[]`). pytest 383 passed·17 skipped.
- 관찰(결함 아님) — 확정하면 설계 재계산으로 **장 나눔이 바뀜**(3장 220~264m → 220~280m).
확정 시점의 장 구성을 고정할지는 판단 대기.
- [X] **돌쌓기 해칭 — 경계로 잘라 싣기**(2026-09-03 사용자 결정). 수확기가 `g[clip-path]`
통째로 건너뛰어 형태 해칭이 CAD 에 하나도 안 실렸다. 선분–폴리곤 교차로 안쪽 조각만 남기고
클립 안 원은 36각으로 펴서 자른다.
- **화면 검증에서 2차 결함 발견·수정**(2026-09-03, 커밋 `0f5b4e4d` — 자동 폴백 커밋에
묶여 메시지가 `auto:` 로 남음). 클립 건너뛰기만 고쳤을 뿐 수확기 selector 가
`polygon, polyline, line, circle, text`**`rect` 를 아예 안 봤다**. 돌쌓기 돌·돌망태
칸은 `cell()``rect` + `rotate(벽기울기)` 로 그리므로 주 표현이 통째로 빠졌다.
실측(용화 route 126 B06 화면) — 형태 해칭 80개 = `line` 31 + **`rect` 49**(61%),
264.06 카드는 `rect` 4(돌쌓기(메) 4켜) + `line` 1.
- 조치 — selector 에 `rect` 추가, `rectPoints()` 로 네 모서리 점열 변환(`rotate(각도 cx cy)`
적용, `rx` 무시). 회전을 빼면 기운 돌이 벽 폴리곤을 벗어나 클립에 전부 잘린다.
- 자체검증 — 3장(220~280m) CAD 구조물 엔티티 **14 → 22개**(돌 8 = 벽 2매 × 4켜).
돌 상자가 켜마다 가로 1mm 씩 밀려 벽 경사를 따름(`-30,-168,-25,-163``-34,-154,-29,-149`).
`tmp/tests/test_cad_rect_harvest.mjs` 4건, pytest 383 passed·17 skipped, typecheck 통과.
- [X] **장 선택 시 정보 패널 표기 정리**(2026-09-03). 장에는 측점 단위 값이 없는데 제목이
「측점 3장 (220~264m)」이었다 — 제목을 「장」으로 바꾸고 안내 문구를 넣었다. 판정은 도면
id(`cross_s…`)로 한다(`chainage_m` 은 장에도 첫 측점 값이 실려 옴).
### B08 토적도·유역도 후속
- [X] **토적도 축척 = 길이별 자동**(2026-09-03 사용자 결정). `auto_scale_h()` — 후보
1:500~1:6,000 중 A1 가용 폭 700mm 에 드는 가장 큰 그림. 실측 40m→1:500 · 용화 1,106m→
1:2,000 · 3,465m→1:5,000. 세로는 종이 1mm=50㎥ 고정.
- [x] **유역 정보표 측점 표기 통일**(2026-09-03, 커밋 `e29e3cab`) — 토적도와 같은 `n+ 0.0`
형식으로 맞춤. `station_plus_label()``_Engine_Cad` 로 공용 이관해 두 도면이 한 함수를 씀.
자체검증 — `tmp/tests/test_b07_masshaul_basin.py` 갱신·통과. **화면 실측 완료**(2026-09-03,
「S자유역검증」 `8879c53b` 유역도) — 정보표 48개 전부 `(1) 4+ 18.3` · `(2) 9+ 16.1`
`(48) 221+ 9.9` 형식.
- [X] **납품 도면 표기 8행 전부 반영**(2026-09-03 사용자 결정) — 종무대 · `Q= ` 한 칸 · 계단형 지시선 · 평형선·경계현 빨강 · `120+ 0.0` · 표 눈금(정규 빨강·추가 회색) · 행 이름 3.2mm 자간 · `0+16.90`.
- 자체검증(2026-09-03, 「구조물검증-LAS」 `2f940d8a` 토적도 엔티티 실측) — **8행 중 6행 확인**:
`Q= 4491.46M3`(한 칸) · 계단형 지시선 `(301.9,-99.8)→(291.8,-99.8)→(291.8,0)`(수평 뒤 수직) ·
측점 `0+ 0.0`~`17+ 10.1` · 표 눈금 빨강 18 / 회색 5(회색 5 = 추가 측점 5개와 일치) ·
행 라벨 `누 가 토 량`·`측 점` 3.2mm 자간 · `M.N= 0+0.00`(소수 2자리).
축척은 `SCALE H=1:600 V=1:50,000` 로 자동 선택.
- **남은 2행은 사례 없음** — 종무대(장비 이름)와 평형선·경계현 빨강은 이 프로젝트에 운반
블록이 없어 도면에 그려지지 않음. 코드 상수는 `BALANCE_COLOR = BAND_CHORD_COLOR =
"#ff4d4d"` 로 확인. 아래 「운반 블록이 있는 실노선에서…」 항목에서 함께 볼 것.
| 항목 | 납품 도면 | 현재 |
| -------------- | -------------------- | --------------- |
| 장비 이름 | 종무대 | 무대 |
| 값 표기 | `Q= 162.41M3` | `Q=162.41M3` |
| 지시선 | 계단형·모서리 접점 | 직선·아래 중앙 |
| 평형선·경계현 | 빨강 | 흰색·노랑 |
| 측점 | `120+ 0.0` | `No.120` |
| 표 눈금 | 정규 빨강·추가 회색 | 없음 |
| 행 라벨 | 큰 글씨·자간 벌림 | 2.2mm 본문 크기 |
| M.N 소수 | `0+16.90` | `0+16.9` |
### B05 종단 계획선 개편 — 검증 후속 (2026-09-02 laptop-main 화면 검증에서 발견)
화면 검증 자체는 완료(실측 수치는 `docs/raw/plans/2026-09-02_plan_user_verified_completed_items_2.md`
「B05 종단 계획선 편집」). 아래 4건은 그 과정에서 나온 **미해결**이다.
완료된 최소고 가드와 구조물 위치 되돌리기는
`docs/raw/plans/2026-09-02_plan_B05_profile_edit_followup_completed.md`로 이동했다.
- 취소 — ~~규칙 측점은 선택이 안 돼 방향키 상하도 안 먹는다~~ **오검(2026-09-02 재확인)**.
`_Profile_Panel.selectStation` 은 같은 측점을 다시 누르면 선택을 푸는 토글이고,
앞선 검증이 240.000 을 두 번 눌러 해제된 상태에서 ArrowUp 을 보낸 것이었다.
한 번만 누르면 `b06-chart__station--selected` 가 붙고 ArrowUp 이 오프셋 0.1 을 만든다.
규칙 측점도 정상 — 코드 변경 없음.
- [X] **`shiftSegment`·`shiftMovingPoints` — 유지**(2026-09-03 사용자 결정). 정리하지 않음. 새 [쉬프트]는
`shiftStraightRuns` 를 쓴다. `_Profile_Alignment.ts` 두 함수의 파일 밖 참조 0건.
「기하 함수는 남김」 방침이라 유지 중 — 되쓸 계획이 없으면 정리 대상(사용자 확인 후).
- 참고 — **`/route/confirm`([저장])은 계획선을 재산출하지 않는다.** 저장 경로는
`_Router_Lifecycle.py` 가 기존 `longitudinal.json` 에 상단측 변경분만 합쳐 다시 쓴다.
새 초기 계획선을 보려면 **[경로 계산]**(`/route/solve`)이 필요하다 — 옛 프로젝트를 열어
기본이 안 바뀐 것처럼 보이는 것은 이 때문이고 결함이 아니다.
### B05/B06 구조물 3D 판단 대기
완료된 패치·마스크 수정은 `docs/raw/plans/2026-09-02_plan_user_verified_completed_items.md`로 이관했다. 2D 면적·유토곡선 산식은 분리 보류이므로 건드리지 않는다.
- [X] **BOX 날개 사이 — 현행(원지반) 유지**(2026-09-03 사용자 결정). 코드 변경 없음.
- [X] **세월교 좌측 접근선 — 패치로 덮음**(2026-09-03 사용자 결정). 접근선(노견 → 벽 상단)은
벽보다 위라 덮어도 관 구멍·L자 통로를 막지 않는다. 벽 전면·유로는 종전대로 리본에 담지
않는다. 접근선 줄을 `patchPoints` 앞에 담고 `fillRuns` 에 한 장으로 실어 「구간 1줄 = 리본
1장」 규칙을 지킨다.
- 자체검증(2026-09-03, 용화 route 126) — `fordSilhouettes()` 를 실제 세월교 2측점
(177.33m·358.92m)에 돌려 **첫 리본이 노견에서 시작**함을 확인: 우측 `(-2, 851.18)`
= `road_edges.right`, 좌측 `(2, 851.30)` = `road_edges.left`, 면마다 `fillRuns` 2장
(접근선 1 + 성토 1). 3D 픽셀 육안은 카메라를 그 측점으로 몰아야 해 미실시 —
리본 데이터로 판정함.
### B05/B06 700줄 제한 위반 정리
- [X] `B06_Section_UI_Section_View.ts` 751 → **689줄** — 유토곡선 조립을 `_MassHaul` 로 분리.
- [X] `B06_Section_UI_Cross_View.ts` 784 → **698줄** — 카드 제목·하단행을 `_Cross_Card_Chrome`,
실효 반폭 계산을 `_Cross_View_Metrics`, 줌 상태 맵을 `_Cross_View_Zoom` 으로.
- [X] `B05_Profile_UI_Profile_Panel.ts` — 현재 **699줄**로 제한 안(별도 작업 불필요).
- [X] `B05_Profile_UI_Structures_Panel.ts` 1,078 → **1,024줄** — 폼 뼈대를 `_Structures_Form`
으로 분리(1차).
- 자체검증 — 분리 3건의 **화면 실동작 확인**(2026-09-03, 용화 route 126):
B06 횡단 카드 65장 전부 header·footer 생성(`_Cross_Card_Chrome`), 유토곡선 패널 표시
(`.b06-masshaul`, `_Section_View_MassHaul`), B05 구조물 배치 폼 본문이
`grid → position-row → divider → grid(옵션) → 시설 서브폼 → 메모 → [추가][삭제][리셋]`
순서로 조립되고 목록 11건 표시(`_Structures_Form`).
### B04 세부유역 중첩·빈공간 — 현행 반영 여부 확인 (2026-09-03, 보조폴더)
사용자 지시: 로직은 그대로 두고, **S자 노선에서 아래 세부유역이 위 세부유역을 포함하는
경우를 현행이 반영하는지 확인만** 할 것.
- [X] **확인 결과 — 셀 단계는 반영, 내보내기 단계에서 소실**.
`assemble_basins`(`common_util/common_util_drainage_detail.py:508`)의 라벨 격자는
셀마다 관 하나만 배정하므로 포함 관계(도넛)가 정확히 살아 있고,
`polygonize_labels`(`B04_PreProcess/B04_PreProcess_Engine_Watershed_Flow.py:526`)도
구멍을 가진 폴리곤을 낸다. 그러나 `largest_ring:563`**외곽 링 하나만** 뽑고
`WatershedBasin.boundary_xy`(`:107`)·API `polygon_lonlat`
(`B04_PreProcess_Router_Basins.py:164,195`)가 **단일 링 자료형**이라 구멍·조각이
실릴 자리 자체가 없음. 즉 화면에서는 아래 유역이 위 유역을 통째로 덮음.
**검증**(`tmp/tests/test_basin_boundary_holes.py`, 1m 격자 합성 사례 3건, 3 passed):
① 도넛 — 갇힌 위 유역 256㎡ 전체가 아래 유역 링에 중첩
② 조각 — 한 관 유역이 두 조각(225㎡+144㎡)이면 작은 144㎡ 소실 = 빈공간
③ 유역별 독립 simplify(2.0m) — 들쭉날쭉한 공유 경계에서 중첩 18.2㎡ · 틈 35.2㎡
(전체 6400㎡ 대비 0.8%). 규칙적 계단 경계에서는 재현 안 됨(양쪽이 같게 단순화).
- [X] **후속 완료(2026-09-03)**: 자료형을 링 목록으로 넓힘.
`polygon_parts()` 신설(조각마다 [외곽, 구멍...], 넓은 조각 먼저) ·
`WatershedBasin.boundary_parts` 로 교체하고 `boundary_xy`(가장 넓은 조각의 외곽) ·
`boundary_rings`(편 링 목록)는 파생 속성으로 남겨 옛 소비처 무손상 ·
API 에 `polygon_rings_lonlat` 추가 · 저장 GeoJSON 은 구멍을 가진 Polygon, 조각이
여럿이면 MultiPolygon · 캔버스 3곳(B04 유역화면 채움·선택, B05 배수유역도 채움·선택)은
even-odd 로 한 번에 채우고 `pointInRings()` 로 판정(구멍 안을 눌러도 바깥 유역이
잡히지 않음). 중복이던 지역 `pointInRing` 은 삭제하고 공용 것으로 통일.
**검증**: 단위 5건 신규(`tmp/tests/test_basin_boundary_rings.py` — 도넛 구멍 보존,
조각 2개 보존, 파생 속성, 빈 경계, **조립 경로 전체에서 유역 하나가 링 2개**),
전체 168 passed. 브라우저 — API 가 22개 유역에 링 목록을 실어 보냄(경계 없는 1개 제외),
캔버스 even-odd 실측(가운데 알파 0 / 고리 255, nonzero 는 255)로 구멍이 실제로 뚫림.
1차 전체 배수유역(분수령)은 `largest_ring` 그대로 — 외곽선 한 줄이 맞는 자리.
- [X] **S자 노선 실사례 검증(2026-09-03, 사용자 지시)**: 용화 지형 위에 스위치백 3구간
합성 노선(CSV, 정점 439·연장 4,528m)을 만들어 신규 프로젝트로 자동 체인까지 완주.
결과 관 48개·세부유역 48개. 도넛은 1건 나왔으나 1㎡짜리 simplify 부스러기였고,
**진짜 빈공간의 원인은 최소면적 필터**로 드러남 — 100㎡ 미만 유역은 폴리곤이 아예
안 만들어져 화면에서 통째로 사라졌음(용화 22개 중 1개, S자 48개 중 1개).
- [X] **최소면적 필터 제거(세부유역 한정)**: `assemble_basins`
`polygonize_labels(..., min_area_m2=0.0)` 로 부름. 실측 면적 오차 용화 5.76%→1.86%,
S자 3.56%→1.42%, 경계 없는 유역 1개→0개, 좌표점은 583→622·806→829 로 거의 그대로.
1차 전체 배수유역·다른 호출부의 기본값(100㎡)은 손대지 않음. 전체 168 passed.
- [X] **단순화 허용오차 2.0m → 1.0m** (2026-09-03 사용자 결정, 메인 창 경유).
격자가 1m(`DRAINAGE_GRID_SIZE_M`)라 경계가 1m 계단이므로 허용오차를 격자 한 칸에
맞춤 — 형상 손실이 격자 오차 안으로 들어감. 「공유 경계를 함께 단순화」(라벨 경계를
선분 그래프로 한 번만 단순화)는 작업량 대비 우선순위가 밀려 이번엔 채택 안 함.
**실측**(min_area=0 상태에서 비교):
| 허용오차 | 좌표점(용화/S자) | 면적오차(용화/S자) | 유역끼리 중첩(용화/S자) | 틈(용화/S자) |
| -------------------- | ----------------------- | ----------------------- | ----------------------- | ----------------------- |
| 2.0m(옛) | 622 / 829 | 1.86% / 1.42% | 1,043㎡ / 1,373㎡ | 1,184㎡ / 2,579㎡ |
| **1.0m(채택)** | **1,749 / 1,987** | **1.01% / 0.67%** | **243㎡ / 373㎡** | **369㎡ / 668㎡** |
| 0.5m | 10,215 / 12,294 | 0.00% / 0.00% | 148㎡ / 129㎡ | 296㎡ / 259㎡ |
| 0.0m | 12,403 / 14,558 | 0.00% | 0㎡ | 0㎡ |
0.5m 이하는 1m 계단 점을 못 지워 좌표점만 6배로 튐. 브라우저 실측도 같은 값
(API 좌표점 용화 1,750 · S자 1,987, 경계 없는 유역 0개), 확대해도 계단이 도드라지지
않음. 테스트 159 passed(B07 항목 아래 별도 기록한 1건 제외).
- [X] **병합 뒤 깨진 로컬 테스트 복구**(2026-09-03): `tmp/tests/test_b07_masshaul_basin.py`
가 사라진 `MM_H` 를 import 해 수집 단계에서 전체 실행이 막혔음. 메인 창이 알려 준
대로 고침 — `auto_scale_h()` 로 축척을 얻고(시험 노선 40m → 1:500, `MM_H = 1000/500`),
표기 6곳을 현행으로(`종무대`·`Q= 600.00M3`·`L= 18.50M`·`EA= 400.00M3`·
`M.N= 1+10.00`·`SCALE H=1:500 V=1:50,000`). 축척 규칙을 굳히는 시험도 추가
(40m→500 · 1,106m→2,000 · 3,465m→5,000 · 길이 0이면 설정 기본값 2,000).
`tmp/tests` 는 git 밖이라 커밋 없음. 전체 **169 passed**.
### B03 계획노선 shapefile — 변환 CSV 정본화 (2026-09-03, 보조폴더)
사용자 확답: 프론트엔드는 **올린 파일 그대로 표시**, 백엔드는 **CSV 로 변환해 사용**하고
그 CSV 를 **초기값(`initial_snapshot/`) 정본**으로 남김. 좌표는 **사업지 좌표계 +
트림·조밀화 후**(2026-09-03 사용자 확정).
현행 조사 결과 — 이미 되어 있는 것(작업 불필요):
슬롯 라벨 `계획노선 도형`(CSV 표기 아님) · `.csv`/`.shp` 동시 수용 · shx/dbf/cpg/route_prj
슬롯 · shp 판독기(`B03_FileInput_Engine_Shapefile.py`) · shp 우선 선택
(`find_planned_route_file`) · 좌표계 변환·트림·조밀화 단일 창구
(`common_util_route_geometry.py:215 load_design_route`).
- [X] 초기값 CSV 기록 — `common_util_initial_snapshot.py`
`planned_route.csv`(sequence,x,y) 를 `save_initial_snapshot()` 이 함께 씀.
값은 체인 2단계가 이미 만든 `points`(사업지 좌표계·트림·조밀화 후)를 그대로 받음 —
재계산·서피스 샘플러 재구축 없음.
- [X] 체인 연결 — `B03_FileInput_Service_Chain.py` 스냅샷 블록이 `points` 를 넘김.
- [X] 정본 사용 — `load_design_route()`**surface_params 가 주어진 호출에 한해**
초기값 CSV 가 있으면 그것을 읽고 반환(재판독·재투영·트림 생략).
`surface_params=None` 호출(도엽 범위 산출 `B04_PreProcess_Engine_SheetSurface.py:492`)은
트림 전 원본이 필요하므로 종전 경로 유지.
- [X] 무효화 규약 확인 — 지표면·노선이 바뀌면 `discard_initial_snapshot()` 이 폴더째
지우므로 CSV 도 함께 사라져 자동으로 shp 재판독 경로로 되돌아감.
- [X] **자체검증 완료** (2026-09-03, 보조폴더 8001·5174)
① 단위 4건 `tmp/tests/test_design_route_csv_master.py` — CSV 왕복 좌표 일치,
정본 우선, 트림 전 호출은 정본 미사용, 스냅샷 폐기 시 경로 닫힘. 전체 158 passed.
② 실사용 경로 — 공용 브라우저에서 프로젝트 신규 등록 → 용화 예정노선 shapefile
5개 + 지형 PRJ·TFW 업로드 → [LAS 없이 설계] 체크 → 업로드 → 자동 체인 완료.
`initial_snapshot/planned_route.csv` 생성됨(643행). 정본 경로와 원본 shp 경로를
맞대 보니 **정점 643개 동일, 최대 좌표차 0.00007m, 연장 2136.2m 동일**.
B05 화면에 종단 계획선·배수유역도(관 22개·세부유역 22개)가 정상 표시.
③ 프론트 표시 — 슬롯 카드가 올린 파일명을 그대로 보임(`대상지_일월.용화.2.2.shp`
외 4개), 라벨은 `계획노선 도형`.
### LAS 없는 설계 — 도엽 서피스가 노선 원본 좌표계로 만들어지던 문제 (2026-09-03, 보조폴더)
**증상**: shapefile 노선(EPSG:5179) + 지형 PRJ(EPSG:5176)로 [LAS 없이 설계]를 돌리면
자동 체인이 `노선 트림: 노선 전체가 지표면 밖입니다` 로 중단됨 — 실측 재현.
**원인**: `B04_PreProcess_Engine_SheetSurface.py:503 run_sheet_surface_analysis`
`read_planned_route()` 로 노선을 **원본 좌표 그대로** 읽어 도엽 서피스를 5179 자리에 만들고
(실측: 서피스 x 1,141,951~1,143,784), 설계 계통 `load_design_route()` 는 노선을 사업지
좌표계 5176(x 208,402~209,637)으로 옮기므로 두 자리가 어긋나 트림이 노선을 통째로 지움.
같은 파일의 `build_sheet_surface_from_route()` 는 이미 `load_design_route()` 를 쓰고 있어
LAS 경로에서는 나지 않던 문제.
- [X] `run_sheet_surface_analysis()``load_design_route()` 로 읽도록 창구 통일,
좌표계는 `planned.crs_input or project_epsg_from_prj()` 를 그대로 사용
(EPSG 라벨 대신 PRJ 원문 WKT 가 실려 TOWGS84 보정이 보존됨).
- [X] 도엽 확보(`download_geodata`)의 PRJ 탐색 폴더를 노선 세트(`input/shp/`)에서
지형 PRJ 폴더(`input/prj/`)로 교정 — 노선 PRJ 가 잡히던 자리.
- [X] **검증**: 수정 후 같은 자료로 재실행 → 체인 완주(노선 643점·2136.2m, 관 22개,
세부유역 22개). 전체 테스트 158 passed.
### B03 [파일 업로드] 가 잠긴 채 남던 문제 (2026-09-03, 보조폴더)
**증상**: 필수 파일을 다 골랐는데도 [파일 업로드] 가 비활성. 새로고침하거나 파일을 하나 더
고르면 풀림.
**원인**(첫 추정 정정): 프로젝트 ID 캐시가 아니었음 — 파일 선택 경로가
`detectPausedUploads()` 에서 ID를 다시 읽어 스스로 풀림. 진짜 자리는 **[LAS 없이 설계]
토글**. 이 토글이 LAS 카드의 필수 여부를 바꾸는데(`isSlotRequired`), change 핸들러가
`updateUploadButton()` 을 부르지 않아 버튼 판정이 옛 상태로 남음. **파일을 먼저 고르고
토글을 나중에 켜는 순서**에서만 재현돼 처음엔 ID 문제로 보였음.
- [X] `lasFreeCheck` change 핸들러에 `updateUploadButton()` 추가 (4줄).
- [X] **검증**(공용 브라우저, 저장된 토글 표시를 지워 첫 진입 상태로 재현):
① 파일 7개만 고른 상태 → 비활성 + "필수 카드를 모두 채우세요 … LAS/LAZ …"
② [LAS 없이 설계] 켠 직후 → **즉시 활성**, 오류 문구 사라짐
③ 다시 끄면 → 즉시 비활성 + 같은 안내. typecheck 통과.
### B04 세부 배수유역 — 종단 높낮이 흐름 반영 (2026-09-03, 보조폴더)
사용자 재정의(2026-09-03): "세밀유역" 표현은 접고, **배수유역 = 횡단배수 지점으로 들어오는
유역**. 빠진 것은 **종단 계획노선의 높낮이에 따른 흐름** — 용화에서 낮은 도로측 유역이
높은 쪽으로 흐르는 것처럼 표현됨.
**진단(용화 실측, 프로젝트 `shp정본검증2`)**: `assign_road_cells_to_pipes` 의 내리막 추적이
한 칸 이웃만 보는 국소 하강이라 1m 간격 계획고의 미세 요철에 걸려 멈춤 — 측점 2,138개 중
**1,898개(88.8%)가 저점에 갇힘**. 갇힌 측점은 흐름이 아니라 **누가거리 최근접**으로 관에
붙어, 도로 셀 11,312개 중 **4,886개(43.2%)가 자기보다 높은 관에 배정**(최대 6.82m).
추적이 성사된 측점의 오르막은 0.8%(최대 0.09m)라 추적식 자체는 정상.
- [X] 배정 규칙 교체 — **관 사이 마루(구간 최고점)가 분수령**. 1차원에서 물은 마루를 넘지
못하므로 마루 왼쪽은 앞 관, 오른쪽은 뒤 관, 첫 관 앞·마지막 관 뒤는 그 관이 받음.
미세 요철에 면역 (사용자 확정).
- [X] 관 없는 저점 126개는 **그대로 둠** (사용자 확정) — 대부분 계획고 미세 요철이지
진짜 사그가 아니고, 마루 분할이 자동으로 무시함.
- [X] **자체검증** ① 단위 5건 `tmp/tests/test_ditch_ridge_assignment.py` — 마루가 경계,
±3cm 톱니에도 배정 불변, 사그는 낮은 쪽으로, 첫·끝 구간, 관 순서 보존. 전체 163 passed.
② 실측 재계산: 오르막 배정 **43.2% → 17.6%**, 최대 상승 6.82m → 4.15m.
③ 잔여 1,993셀은 전부 **사그에 고였다가 더 낮은 장벽(=배정된 관) 쪽으로 넘치는**
경우임을 확인(어긋남 0개) — 물리적으로 옳은 배정이라 남겨 둠.
④ 화면: B05 배수유역도 [유역 다시 나누기] 후 유역 면적이 실제로 바뀜
(관1 1.35ha → 1.07ha, 관3 2,545㎡ → 3,661㎡, 관4 23.34ha 세월교 제안).
### B04 지표면 3D 뷰어 방위 콤파스 (2026-09-03, 보조폴더)
사용자 확인: 대상은 **B04 지표면 3D 뷰어** (B03 에는 3D 화면이 없음).
- [X] `B04_PreProcess_UI_Compass.ts` 신설 — 카메라 오프셋으로 도북 각도를 내는 작은 모듈.
뷰어 좌표 규약이 `x, 높이, -y` 라 세계 북쪽은 `-z`, 화면 회전각은
`atan2(offset.x, offset.z)`. 뷰어 파일이 이미 946줄이라 별도 파일로 뺌(700줄 제한).
- [X] `B04_PreProcess_UI_TerrainViewer.ts` 배선 3줄 — 뷰어 우하단(축척 막대 반대편)에 얹고
애니메이션 루프에서 각도 갱신(0.5° 미만 변화는 건너뜀). 지표면이 보일 때만 표시.
- [X] CSS — `.b04-surface__compass*` (테마 토큰 사용, 라이트·다크 공용).
- [X] **자체검증**: `npm run typecheck` 통과. 공용 브라우저에서 ① [상단] 시점 회전각
`matrix(1,0,0,1,0,0)` = 0° (바늘이 화면 위 = 북쪽, 북쪽 위인 2D 지도와 같은 방향)
② 캔버스를 220px 드래그하니 `matrix(-0.78152,-0.62388,…)` = 218.6° 로 카메라와 함께
회전 ③ 상단·하단 두 3D 뷰어 각각에 하나씩(총 2개) 표시. 검증 후 시점은 기본(사시도)
으로 되돌려 둠.
### 프로젝트 작업 좌표계 일원화 (2026-08-31 신규)
- [X] **작업 좌표계 = 지형 PRJ 로 확정**(2026-09-03 사용자 결정 — 서피스 격자가 모델좌표의
주인이라 현행 유지). 「노선 .prj 우선」은 노선 UTM-K / 지형 동부원점 Bessel 로 실제 좌표계가
달라 서피스·격자를 통째로 재투영해야 해 폐기.
- [X] **폴백 사다리를 창구 하나로 통합**`common_util_crs.resolve_project_crs()` 신설.
① 노선 crs_input ② 파일 라벨(원본 좌표를 읽는 자리만) ③ 지형 PRJ ④ DB epsg ⑤ EPSG:5186.
`_route_center_lonlat` 이 CSV 라벨을 그대로 변환에 쓰던 자리를 이 사다리로 교체.
`tmp/tests/test_project_crs_resolution.py` 5건 신설.
- [X] **트림 실패 진단** — 지표면 트림이 노선을 통째로 지우면 노선·지표면 bbox 를 함께 로그에
남긴다(겹침 0이면 좌표계, 일부 겹치면 측량 범위 문제).
- [ ] ~~`planned.epsg or 5186` 폴백이 여섯 군데 흩어져 있다~~ (완료 — 위 항목):
`B04_PreProcess_Engine_SheetSurface.py:467·496`, `B04_PreProcess_Router_Watershed.py:186·253`,
`B04_PreProcess_Router_Inflow.py:84`, `common_util_drainage_context.py:98`.
`B04_PreProcess_Engine_Extent.py:147-152`, `B04_PreProcess_Engine_GisVector.py:148`도 5186 기본값.