- `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>
19 KiB
19 KiB
B04/B05 배수유역 일원화 + 종단 테이블 구조물 연동 (2026-08-01 착수)
목표: 배수유역 데이터를 B04 한 곳으로 모으고, B05는 그 데이터를 그대로 받아 같은 것을 그린다. 관 매설 지점은 종단 테이블의 구조물 라인으로도 다뤄진다.
A. 조사 결과 — 현재 데이터 흐름의 문제 (2026-08-01 확인)
| 항목 | 실제 상태 | 문제 |
|---|---|---|
| 해석 산출물 | sync_from_b04()가 B04_wf1_Surface/drainage/ 7개 파일을 B05_wf2_Route/drainage/로 전량 복사 |
디스크 2배(7.2MB 중복), 갱신 시점이 B05 진입에 묶임 |
| 관 지점 정본 | B04_.../drainage/edits/pipe_points.json |
B05는 이 파일을 전혀 참조하지 않음(grep 0건). B04에서 확정한 관이 B05에 안 보이고, B05 진입 시 자동 배치로 되돌아감 |
| API | B04 GET/PUT /drainage/pipe-points + POST /drainage/detail-basins, B05 POST /drainage/basins |
같은 엔진인데 엔드포인트·응답 모양이 2벌 |
| 사본이 생긴 이유 | "B05 편집이 B04 원본을 덮어쓴다" | 외곽선 편집을 제거(e994330)해 B05는 이제 아무것도 저장하지 않음 → 사본 존재 이유 소멸 |
| 고아 데이터 | B05_.../debug/(삭제된 Subdivide 엔진 검증 스크립트 4 + PNG 2 + JSON 1) |
2026-08-01 삭제 완료 |
| 좌클릭 제한 | B04는 event.button === 0 확인 후 마커를 잡음. B05는 버튼 검사 없이 pipeEditor.handleDown() 호출 |
B05에서 우클릭·가운데 클릭에도 마커가 잡힘 |
| 유역 목록 높이 | .b05-drainage__basins { max-height: 34% } |
비율 기반이라 유역 수와 무관하게 잘림 |
| 구조물 측점 | IrregularStation{id, station, remainder, chainage_m, structure} — 클라이언트 전용, 경로 확정 시에만 백엔드 전달 |
관 매설 지점과 아무 연결 없음 |
B. 설계 결정 (사용자 확정)
- 저장소: B05 사본 폐지,
B04_wf1_Surface/drainage/단일화. 두 화면이 같은 폴더·같은edits/pipe_points.json을 본다. - API: B05 전용
POST /drainage/basins폐기. 두 화면 모두GET /drainage/pipe-points·POST /drainage/detail-basins·PUT /drainage/pipe-points사용. - 종단 테이블 배관: 구조물 목록에 실제 항목으로 등록(이름 "배관"). 단 저장 정본은
pipe_points.json이고 목록의 배관 항목은 그 투영이다 — 관 위치 변경은 어느 쪽에서 시작하든pipeEditor를 거쳐 한 경로로 전파한다. - 고아 데이터: 1회성 수동 정리.
debug/삭제 완료, B05drainage/사본은 사본 폐지 구현 마지막에 삭제(먼저 지우면sync_from_b04가 다시 만든다). - 버튼 형태 유지: B05 배수유역 패널의 버튼 스타일(
b05-drainage__analyze등)은 그대로 두고 기능만 맞춘다.
C. 구현 체크리스트
C-1. 저장소·API 일원화 (선행)
B05_wf2_Route_Engine_Drainage_Store.py삭제 —sync_from_b04()·b05_drainage_dir()제거B05_wf2_Route_Engine_Drainage_Basin.py삭제 — 사본 경로 어댑터라 존재 이유가 사라짐B05_wf2_Route_Router_Drainage.py삭제 +main.py라우터 등록 제거config_system.py의DRAINAGE_B05_DIRNAME제거- B04
Router_Basins가 B05 요청도 받도록 확인 — 확정 경로가 없어도 동작하므로 추가 조건 불필요(load_drainage_context가 route 없으면 CSV 기준으로 폴백) - 저장 시점 정리: B04는 "모델 확정", B05는 "경로 확정"에서
PUT /drainage/pipe-points호출
C-2. B05 프론트 API 전환
B05_wf2_Route_Api_Fetch.ts의fetchDrainageBasins/DrainageBasinResponse제거 → B04 클라이언트(fetchDetailPipePoints·computeDetailBasins·saveDetailPipePoints) 재사용_UI_Drainage_Panel.ts응답 매핑 교체(pipe_points/basins/strength_profile/main_polygon_lonlat등)- B04가 주는 값 중 B05에 없던 것 반영: 2차 전체 유역 외곽선은
detail.basin_lonlat, 상류 세류망·평균 흐름 화살표는 기존과 동일 키 확인
C-3. B05 UI 일원화 (버튼 형태 유지)
플롯 대상 — 도엽등고선(있음) · 평균 흐름(있음) · 흐름 강도(있음) · 세부유역(있음) · 관 제어(있음) · 유역 선택(목록만 있음)
- 유역 폴리곤 클릭 선택 추가 — 현재는 목록 행 클릭만 가능. B04와 같은
pointInRing판정으로 지도에서도 고르고, 유역 밖 클릭 시 해제 - [집중유역] 토글 추가 (제안 반영) — 유입 집중점 마커. 관을 어디에 둘지 판단하는 근거라 사용자 화면에도 필요. 기본 꺼짐, B04와 같은 규칙
- 종단 Z 출처·관 개수 요약 표기 (제안 반영) — 세부유역이 갈리는 근거라 화면에서 확인 가능해야 함
- 관 마커 색을 B04와 같은 기준(기본/자동/수동)으로 통일할지 검토 — 현재 B05는 유역 파스텔색, B04는 생성 사유색. B05는 유역 대응이 중요하므로 현행 유지하고 마커 테두리로만 사유를 구분
C-4. 좌클릭 전용 이동 (B04·B05)
- B05
pointerdown에서event.button === 0일 때만pipeEditor.handleDown()호출 pipeEditor.handleDown()자체에도 방어 조건은 두지 않고 호출부에서만 거른다(우클릭 메뉴가 같은 좌표 판정을 쓰기 때문)- B04는 이미 좌클릭 전용 — 회귀만 확인
C-5. 유역 목록 3행 스크롤
.b05-drainage__basins의max-height: 34%를 행 높이 기준calc(3 * 행높이 + 패딩)으로 교체. 행 높이는 CSS 변수로 한 곳에 정의
C-6. 종단 테이블 구조물 라인 연동
_UI_IrregularStations.ts— 외부에서 "배관" 항목을 통째로 갈아 끼우는 API 추가(setPipeStations(chainages)), 사용자 수동 항목과 구분되는 표식(origin: "pipe" | "user")- 관 목록 → 구조물 목록 투영: 유역 산정 결과가 오면 "배관" 항목을 그 누가거리로 재생성
- 테이블 라인 드래그 이동 —
_UI_Profile_Table.ts의is-floating컬럼에 포인터 드래그 부착. 배관·기타 구조물 모두 이동 가능- 배관을 옮기면 →
pipeEditor갱신 → 세부유역 즉시 재산정(지도와 같은 경로) - 기타 구조물은 목록 chainage만 갱신
- 배관을 옮기면 →
- 테이블 우클릭 메뉴 —
ui_template_context_menu.ts재사용. 라인 위: "배관 삭제"/"구조물 삭제", 빈 자리: "배관 추가" - 이름은 이번 범위에서 "배관" 고정. 드롭다운·수기 입력은 후속(현재 자유 텍스트 입력 폼은 그대로 둔다)
C-7. 정리·마감
B05_wf2_Route/drainage/사본 폴더 삭제(C-1 완료 후, 재생성되지 않는 것 확인하고)_Engine_Drainage_Basin.py주석에 남은boundary_overrides.json언급 제거(파일 삭제로 자연 해소)ruff format/prettier/ruff check/tsc --noEmit/vite build- 700줄 규칙 확인 — 위험 파일:
_UI_Profile_Table.ts(563줄, 드래그·메뉴 추가 예정),_UI_Profile_Panel.ts(690줄, 배선 추가 예정). 초과 시 즉시 분리 - 서버 기동 검증 후 커밋 →
.claude/git-autopush.ps1
C-8. 구현 중 추가된 분리 (700줄 규칙)
작업 중 세 파일이 한계를 넘어 분리했다.
| 파일 | 전 → 후 | 분리 대상 |
|---|---|---|
_UI_Drainage_Panel.ts |
787 → 690 | _UI_Drainage_Render.ts(캔버스 렌더러), _UI_Drainage_Parts.ts(토글 줄) |
_UI_Profile_Panel.ts |
729 → 669 | _UI_Profile_Data.ts(종단 자료 어댑터) |
_UI_Page.ts |
702 → 683 | 세션 캐시 헬퍼를 _Api_Fetch.ts로 이동 |
_UI_Profile_Table.ts |
563 → 577 | 구조물 라인은 _UI_Profile_Structures.ts 신설로 흡수 |
D. 검증 기준
- B04에서 관을 옮기고 확정 → B05 진입 시 같은 위치에 같은 개수의 관과 같은 세부유역이 보인다.
- B05에서 관을 옮기면 지도·유역 목록·종단 테이블 배관 라인이 동시에 움직인다.
- 종단 테이블에서 배관 라인을 끌면 지도의 관 마커와 세부유역이 따라 바뀐다.
storage/.../B05_wf2_Route/drainage/가 다시 생기지 않는다.- B05 지도에서 우클릭·가운데 클릭으로는 관이 잡히지 않는다.
- 유역이 4개 이상이면 목록에 스크롤이 생기고 3행까지만 보인다.
D-1. 실행 검증 결과 (2026-08-01, 서버 기동 상태)
| 확인 항목 | 결과 |
|---|---|
| 통합 엔드포인트 | GET/PUT /drainage/pipe-points · POST /drainage/detail-basins · GET /drainage/primary-region · GET /drainage/road-inflow |
POST /drainage/basins(B05 전용) |
404 — 폐기 확인 |
| 통합 응답 필드 | pipe_points 4 · basins 4 · inflow_hotspots 10 · flow_arrows 377 · upstream_lonlat 16 · main_polygon_lonlat 122점 · strength_profile · z_source — 누락 없음 |
| B05 사본 재생성 | 삭제 후 API 호출해도 다시 생기지 않음 |
| 품질 게이트 | ruff check 통과 · tsc --noEmit 통과 · vite build 성공 |
| 700줄 규칙 | 배수유역·종단 관련 파일 전부 통과(최대 _UI_Drainage_Panel.ts 690줄) |
화면 조작(테이블 라인 드래그·우클릭, 유역 클릭 선택, 3행 스크롤)은 브라우저에서 사용자 확인이 필요하다.
F. 사용자 확인 후 후속 조치 (2026-08-01, 즉시 수정)
화면을 보고 내려온 지시 3건. 구조물 조작의 자리를 테이블에서 그래프로 옮겼다.
- 테이블 세로선 제거 — 구조물 위치는 그래프 측점선이 이미 보여 주므로 테이블에까지 얹으면 같은 것을 두 번 그리는 셈.
_UI_Profile_Structures.ts의 라인 레이어와 관련 CSS를 걷어내고 우클릭 메뉴만 남겼다(mountStructureLines→mountStructureMenu). - 드래그 위치 이동 — 종단 그래프의 구조물 측점선을 끌어 옮긴다.
createLongitudinalProfile()에onDragStation인자를 추가하고,kind === "irregular"측점선에만 끌기를 붙였다(attachStationDrag, 좌클릭 전용·3px 이하는 고르기). 화면 x → 누가거리 역변환은inverseOf(). - 좌측 사이드 패널 편집 전파 — 구조물 폼에서 "배관" 항목을 고치거나 지우면 그래프·배수유역도가 함께 갱신된다.
DrainagePanel.setPipeChainages()신설, Page의applyIrregularStations()에서origin === "pipe"항목만 뽑아 넘긴다.- 되먹임 고리 차단:
reconcilePipes()가 현재 목록과 같으면null을 돌려 아무 일도 하지 않는다(관 목록 → 구조물 목록 → 관 목록으로 돌아오는 순환이 여기서 끊긴다). 생성 사유(기본/자동/수동)는 자리로 맞춰 이어 붙인다.
- 되먹임 고리 차단:
- 손으로 넣은 "배관"도 관 지점 정본을 따르게 — 사이드바 폼으로 이름을 "배관"이라 적어 넣은 항목은
origin: "user"라 [초기화]로 지워지지 않고 배수유역도와도 어긋난 채 남았다.isPipeStation()(origin이 pipe이거나 이름이 "배관")으로 판정을 통일해 목록 교체·이동·삭제·선택 동기화가 모두 같은 규칙을 쓴다. - 첫 진입 시 배수유역도가 비어 보이던 문제 — 대시보드에서 곧장 들어오면 패널이 배치되기 전(0×0)에
fitToRoute()가 돌아 엉뚱한 배율이 굳었다(새로고침하면 보이던 이유). 크기가 2px 미만이면 맞춤을 미뤘다가 첫 배치 때 다시 맞춘다(fitPending). - 관을 옮겨도 옛 자리 세로선이 남던 문제(진짜 원인) — 구조물 측점이 두 곳에 있었다. ① 화면 정본인 사이드바 목록(
irregularStations), ② 경로 확정 때 종단 정본 파일에 병합된kind: "irregular"측점(_Router_Confirm._merge_irregular_into_longitudinal). 그래프(normalizedLongitudinal(...).stations)와 3D(renderStationLines)가 둘을 겹쳐 그려서, 관을 옮기면 ①만 따라가고 ②는 확정 당시 자리에 그대로 남았다. 앞선isPipeStation()수정이 ①만 다뤄 증상이 남아 있었다.- 확인:
storage/1/3/{project}/B06_wf3_ProfileCross/longitudinal/longitudinal.json에배관94.97 / 149.73 / 200.92 / 275.71이 저장돼 있었고, 현재 자동 배치는 84.3 / 149.73 / 200.92 / 275.71 — 화면에 남던 유령이 94.97이다. - 수정: 그래프·3D 모두 종단 정본의
kind === "irregular"를 걷어내고 사이드바 목록만 얹는다(_UI_Profile_Panel.draw(),_UI_Page.renderStationLines()). 저장분은 진입 시restoreSections()가 목록으로 되살리고, 관 항목은 배수유역도가setPipeStations()로 정본(pipe_points.json)에 맞춰 갈아 끼운다. 두 저장소는 [모델 확정] 시점에 다시 합류한다(confirm()이savePipes()→confirmRoute(irregular_stations)순으로 보낸다).
- 확인:
- 2D 화면 휠 방향 반전(전 페이지 통일) — 휠을 당기면 확대, 밀면 축소. B04 2D 지도(
_UI_MapViewer.ts)·B05 배수유역도(_UI_Drainage_Panel.ts)·B06 횡단도(_UI_Cross_View.ts)는deltaY < 0→deltaY > 0으로, B07 CAD는input-controller.ts의zoomScreen(evt.deltaY)→zoomScreen(-evt.deltaY)로 뒤집었다(openwebcad 계산식은 손대지 않는다 — 부호만 넘긴다). 커서 고정 보정은 모두 그대로. 3D 뷰어(_UI_Camera.ts)는 지시 밖이라 손대지 않았다.- B07은 별도 번들이라
npm run build:b07-cad재실행이 필요하다(수행 완료).
- B07은 별도 번들이라
- 측점 선택 하이라이트가 표를 가리던 문제 — 하이라이트 열이 덮는 자리의 규칙 측점 값 셀을
hideCoveredCells()로 통째로 숨겨(visibility: hidden), 비정규 측점을 고를 때마다 좌우 최대 2개 열의 값이 사라졌다. 표는 그대로 두고 선택된 값만 위에 얹도록 그 함수와.is-coveredCSS를 걷어냈다(2026-08-02 사용자 지시). - 하이라이트 열의 계획고 추가 강조 삭제 — 계획고만 자주색 테두리·글자색으로 따로 강조돼 같은 열의 다른 값과 달라 보였다. 평상시에는 다른 셀과 같은 모습이고, 고칠 수 있다는 표시는 hover·focus에서만 나타나게 했다(
.b05-profile-table__irregular-input). 직접 입력 기능은 그대로. - [초기화]가 저장분을 남기던 문제 — 화면만 자동 배치로 되돌리고
edits/pipe_points.json은 그대로 두어, 다시 들어오면 옛 관이 살아났다(사용자 보고).DELETE /{project_id}/drainage/pipe-points신설 — 저장분과 파생물(04_detailed_basins.geojson)까지 지우고 자동 배치 결과를 돌려준다. B05 [초기화] 버튼이 이 경로를 쓴다.- 확인: 초기화 전
{user:1, stream:1, spacing:2}· 초기화 후{stream:1, spacing:3}· 두 파일 삭제됨 · 재조회도 같은 자동 배치.
- 확인: 초기화 전
- B05 유역 영역 클릭 선택 복구(회귀 수정) —
pointerdown에서basinClickStart를 두는 코드가 실제로는 들어가지 않아, 클릭 후보가 항상null이 되어pickBasinAt()이 한 번도 불리지 않았다. 같은 편집에 묶여 있던 B05 좌클릭 전용 제한(C-4) 도 함께 누락돼 있었다(포맷 변경 뒤 문자열이 어긋나 치환이 적용되지 않은 것). 둘 다 넣고 적용 여부를 grep으로 확인했다. - B04 집중유역 오버레이를 최상단으로 —
FlowStrengthOverlay.draw()를 강도 색칠만 남기고, 집중점 마커·유입 외곽선은 새drawTop()으로 떼어 세부유역·관 마커 뒤에 그린다. 켰을 때 무엇에도 가리지 않아야 고를 수 있다. - 패널 크기를 바꿔도 지도 배율 고정 — 하단 패널·우측 배수유역 패널을 늘리면
computeMapRect()가 지도를 뷰포트에 다시 맞춰(contain) 함께 확대돼, 방금 보던 자리를 다시 찾아야 했다. 화면 변환 계수(ax·bx)가 그대로가 되도록 배율·오프셋을 되계산해 늘린 만큼 더 넓은 범위를 보여 준다(preserveViewOnResize()·observeViewportSize()in_Drainage_Parts.ts). - 흐름 강도 색띠 범례(게이지 바) — 지도 우측에 세로로 세운다. B04 2D 지도·B05 배수유역도 공용(
createFlowLegend()in_UI_FlowRamp.ts,ui_template_flow_legend.css). 색은 화면과 같은 색띠, 눈금은 같은 로그 정규화(expm1(t·log1p(max)))로 되돌려 적는다 — 다른 규칙으로 그리면 범례가 오독을 만든다. 제목은 "흐름강도". - 배관 누가거리 라벨 가독성 — 흰 테두리(
--map-halo)를 깔고 그 위에 글자를 얹는다.--map-*토큰은 다크 테마에서 바뀌지 않으므로 색만 바꿔서는 위성사진·다크 배경에서 묻힌다. - 선택 3자 동기화 — 배수유역 영역 ↔ 종단 그래프 세로선 ↔ 좌측 구조물 폼(+3D 마커)이 서로를 갱신한다.
_UI_Selection.ts신설로 네 곳의 선택 경로를 한 모듈에 모으고,isSyncing가드로 재진입을 막는다. 배수유역 강조는origin === "pipe"항목에만 붙는다(다른 구조물에는 대응 유역이 없다).DrainagePanel: 유역 강조를 바꾸는 자리를selectBasin()하나로 모으고, 밖에서 고를 통로selectBasinByChainage()와 알림onBasinSelected를 냈다.
- 유역 서클 번호를 최상단 오버레이로 — 채움과 함께 그리던 것을 떼어내 맨 마지막에 얹는다. B04는
drawBasinNumbers()호출을 관 마커 뒤로 옮겼고, B05는drawFilledRing()에서 라벨을 빼고(drawRingBadge()·ringCenterOnScreen()신설) 배관 마커 뒤에 그린다. 등고선·화살표·관 마커에 가리지 않는다. - 우클릭 메뉴도 그래프로 이동 — 배관 추가·삭제를 테이블에서 떼어 종단 그래프 칸(
.b05-profile__chart)에 붙였다. 구조물 조작(이동·추가·삭제)이 전부 한 자리에 모인다. 측점선 근처면 "배관/구조물 삭제", 빈 자리면 "배관 추가"(선 대신 화면 거리로 판정). 테이블은 값만 보여 준다. - 700줄 규칙 유지를 위한 추가 분리:
reconcilePipes()·fetchDrainageLayers()·fitViewToRoute()→_Drainage_Parts.ts, 오버레이 도형(drawFilledRing·drawRingBadge·drawRidgeRing·drawUpstreamLines·ringCenterOnScreen) →B04_wf1_Surface_UI_MapOverlays.ts신설, 선택 동기화 →_UI_Selection.ts신설.
E. 미결 (이번 범위 밖)
- 구조물 이름 드롭다운/수기 입력 — 이번엔 "배관" 고정. 그 외 미결은 '향후 작업'에 있다.