Commit Graph
187 Commits
Author SHA1 Message Date
eomsangdonandClaude Opus 5 d3b443e077 feat(B04): 배경 지도 여유폭을 미터가 아닌 주변 셀 겹수로 지정
사용자 요청 방향(2026-08-01): 해상도(zoom 18)는 그대로 두고, 계획노선과 기준 좌표를
덮는 타일 바깥으로 주변 셀을 더 확보한다.

- SURFACE_MAP_MARGIN_M(300m) 제거, SURFACE_MAP_MARGIN_TILES(3겹) 신설.
  기존 하드코딩 1겹 -> 설정값. zoom 18에서 1겹 약 122m(위도 37deg).
- download_extent는 덮어야 할 범위(라이다 U 계획노선)만 돌려주고 여유는 타일 쪽에서 준다.
- 표본 실측: 덮을 범위 372x401m -> 10x11 타일 = 1220x1341m,
  계획노선 사방 여유 474~588m (이전 731x853m).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 12:11:39 +09:00
eomsangdonandClaude Opus 5 5073916739 fix(cache): 브라우저 보관함 버전을 올려 철회된 도엽 표시본을 비운다
도엽 표시용 사본을 철회했지만, 이미 그 사본을 받아 둔 브라우저는 계속 그것을 쓴다
(보관함 키가 주소 기준이라 서버 파일을 지워도 남는다). DB_VERSION 1 -> 2로 올리고
업그레이드 시 기존 저장소를 버리도록 해 다음 접속에서 원본을 새로 받게 한다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 12:06:23 +09:00
eomsangdonandClaude Opus 5 d033e58880 revert(B04): 도엽 표시용 사본 철회 — 도엽 산출물을 원본 그대로 전송
배수유역이 어디까지 뻗을지 알 수 없어 도엽 자료를 잘라 두 벌로 나누지 않는다
(2026-08-01 사용자 지시). 분석 경로는 원래부터 원본을 읽었고 사본은 전송용이었으나,
사본 자체를 없앤다.

- B04_wf1_Surface_Engine_SheetView.py 삭제, SHEET_VIEW_* 설정 제거
- /geojson의 도엽 레이어는 원본 파일 그대로 + ETag로 전송
  (재직렬화 제거만으로 요청당 2.0초 -> 0.006초, 두 번째 요청부터 304)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 11:57:12 +09:00
eomsangdonandClaude Opus 5 07a2cf1393 fix(B04): 배수유역 재산정 버튼이 표시 토글로 걸려 눌러도 반응 없던 문제
저장분을 자동 표시하도록 바꾼 뒤로 진입 시 shown=true가 되어, 재산정 버튼을 누르면
분석 요청 없이 오버레이만 숨겨졌다(사용자 눈에는 무응답). 버튼은 언제나 재산정을
실행하도록 하고, 보이기/숨기기는 갈래별 버튼(1차/2차/유역방향/평균흐름)이 맡는다.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 11:54:07 +09:00
eomsangdonandClaude Opus 5 a9caf872b2 perf(B04/B05): 도엽 표시본 도입 + 배경 범위·도엽 기준을 계획노선으로 변경
- 도엽 병합본의 화면 표시용 사본 생성(B04_wf1_Surface_Engine_SheetView):
  프로젝트 + 500m로 잘라내고 좌표를 소수 7자리로 줄여 등고선 37.3MB -> 0.97MB.
  분석용 원본은 그대로 둔다(배수유역은 상류까지 넓은 범위가 필요).
- /geojson의 도엽 레이어를 파일 그대로 + ETag로 전송 — 매 요청 2.0초 재직렬화 제거(0.008초).
- /vworld-map도 ETag 전송으로 바꾸고, 프론트가 붙이던 `&_t=` 시간꼬리표 제거.
- 도엽 레이어를 브라우저 보관함 경유로 조회하고 준비화면에서 미리 담는다.
- 배경 지도 범위 = 라이다 범위 + 계획노선 범위 + 여유 300m(SURFACE_MAP_MARGIN_M).
- 도엽 기준 좌표 = 계획노선 시점·종점(없으면 라이다 중심). 같은 도엽이면 9매,
  이웃 도엽에 걸치면 12매. 표본 프로젝트는 기존 9매와 동일(회귀 없음).
- 선정에서 빠진 도엽 zip 정리(prune_sheets) — 표본에서 30매 중 21매가 다른 지역 잔재.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 11:47:29 +09:00
eomsangdonandClaude Opus 5 96561de120 perf(B04/B05): 확정 지표면 요약 API 신설 + 포인트클라우드 24MB 수신 제거
- GET /surface/confirmed 추가: 확정 구성(모델·필터·표현·평활·등고선간격)과
  지형 가장자리만 반환(수 KB, 0.02s). 진입 판정·준비화면·B05의 단일 출처.
- B05 진입이 받던 포인트클라우드 JSON 23.8MB 제거 — 실제로 쓰던 값은 bounds뿐.
- 준비 표식을 프로젝트 ID에서 확정 signature로 변경: B04에서 다시 확정하면
  대시보드 복귀·새 브라우저·B그룹 단계 이동 어느 경로로 들어와도 최신본을 담는다.
- preloadSurfaceAssets가 평활 여부를 추측하던 부분 제거(확정 저장값 사용) —
  추측이 어긋나면 같은 지형을 두 번 내려받았다.
- B04 진입 시 필터·표현·평활·등고선간격을 확정본 값으로 초기화(확정 없으면 기존 기본값).
- 모델 확정 직후 준비 표식과 B05 세션 캐시를 비워 옛 지형이 남지 않게 함.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 11:34:44 +09:00
eomsangdon 77a637d7c9 feat(B11): 자료 준비 화면 신설 — 작업 화면 진입 전 3D·등고선 선적재
- B11_Status_UI_Loading + 라우트 b11-loading: 공통 프로그레스 서클로 단계·진행률 표시,
  끝나면 원래 가려던 화면으로 자동 이동
- goToWorkflowStage: 아직 준비하지 않은 프로젝트면 준비 화면 경유, 같은 프로젝트로
  다시 들어오면 건너뜀(탭 단위 기억). 다른 프로젝트면 보관분 교체
- 선적재 범위: 확정 지표면 3D + 그 등고선만 (종단·횡단은 0.86MB·0.06초로 이미 즉시라 제외,
  배수유역·포인트클라우드도 제외)
- 실패 시 자동 이동하지 않고 담당자 연락 안내 + [그래도 이동]/[대시보드로]
- B04 등고선 간격 시작값을 B05 저장값과 공유(B04 변경은 DB에 쓰지 않음)
- 새 문구는 ui_locales에 한국어·영어 등록
2026-08-01 10:34:08 +09:00
eomsangdon c970812cf3 feat(3D): 휠 방향 반전·회전점 표시 + 3D/등고선 브라우저 보관함
- 휠 위 = 축소로 반전, 커서 지점을 축으로 한 dolly를 공용 유틸에서 직접 처리
- 회전 중심을 작은 구로 표시(돌리는 동안만, 화면상 크기 일정, 항상 위에 그림)
- 등고선 렌더 비용 감소: 폴리라인을 주곡선·보조곡선 2덩어리로 병합(드로우콜 424 → 2),
  라벨은 카메라가 움직였을 때만 재배치
- common_util_http_cache: 파일 mtime+크기 ETag, If-None-Match 일치 시 304
  (preview·contour 적용, 파일이 바뀌면 자동 무효화)
- A00_Common/b_asset_cache: IndexedDB 보관함(키 = projectId|url, 값 = 바이트+ETag).
  보관본 즉시 사용 후 백그라운드 재검증, 3D는 보관 바이트를 직접 파싱
- 프로젝트 전환 시 타 프로젝트 보관분 삭제, 대시보드→B그룹 이동 시 확정 모델의
  3D 프리뷰·등고선(1.0m) 미리 받기(포인트클라우드 제외)
2026-08-01 09:58:44 +09:00
eomsangdon 13c2522f47 feat(B04/B05): 프로그레스 서클을 3D·지도·그래프 전 영역에 공통 적용
- 공통 서클 양식을 로딩 스피너(.ui-spinner) 기준으로 통일하고 overlay 옵션 신설
  (컨테이너 정중앙 배치를 페이지별 CSS 없이 처리)
- B05 3D: 위쪽 18% → 뷰포트 정중앙 (하단 패널에 가려져도 무방)
- B04 포인트클라우드 뷰어: setLoading() 신설, render() 시 자동 해제
- B04 지형 뷰어: GLTF/PLY 로더 진행 이벤트로 실제 바이트 진행률 표시
- B04 2D 지도: 도엽 레이어 n/10 진행률
- B05 종단면 그래프: body-wrap으로 감싸 서클 유지, 자료 조회 전후로 토글
- B05 배수유역도: 배경도 → 도엽 레이어 → 세부유역 산정 단계 진행률
2026-08-01 09:29:53 +09:00
eomsangdon f6be8d7307 fix(B04/B05): 3D 조작감 보정 — 커서 표면 회전축, 상하 반전, 가운데 버튼 팬
- 회전축을 커서가 가리키는 지형 표면 지점으로. 포인트클라우드는 카메라 거리의 1%를
  클릭 허용 반경으로 주고, 못 맞히면 시선 수직 평면과 만나 커서 방향 지점을 쓴다
  (화면 중앙으로 되돌아가지 않음)
- 수직 회전 부호 반전 (마우스를 내리면 내려다보는 방향)
- controls.mouseButtons.MIDDLE = PAN (전역 공통). 브라우저 자동 스크롤 차단
- B05 뷰셋 버튼 그룹을 좌상단으로 이동 (.b05-route__view-controls top 58px → 16px)
2026-08-01 09:19:59 +09:00
eomsangdon 4501f3b19e auto: 2026-08-01 08:55 (EOMSANGDON-HOME) 2026-08-01 08:55:01 +09:00
eomsangdon 4aa211cead feat(drainage): B05 평균 흐름 화살표 표기 + 응답 캐시 저장 누락 수정 2026-07-31 21:47:17 +09:00
eomsangdonandClaude Fable 5 500a3c9047 fix(B04): 배수유역 상태 문구 전용 줄 분리 + 저장분 없을 때 안내
증상 두 가지
  1. 재산정 버튼을 눌러도 프론트에 아무 안내가 없었다.
     오버레이가 지도 자체 상태(.b04-map__status)에 문구를 썼는데,
     loadLayers 가 같은 칸에 3곳에서 덮어써 즉시 지워졌다.
  2. 페이지 접근 시 배수유역이 안 보였다.
     응답 캐시 파일(00_watershed_response.json)이 아직 없어 자동 조회가
     빈 손으로 끝나는데, 그 사실을 화면에 알리지 않아 이유를 알 수 없었다.

조치
- 배수유역 전용 상태 줄(.b04-map__watershed-status)을 컨트롤 영역에 따로 둔다.
  지도 로딩 문구와 서로 덮어쓰지 않는다.
- 컨트롤에 배수유역 그룹을 만들어 재산정 버튼 + 갈래 토글 4종 + 상태 줄을 묶었다.
- 진행 상태를 버튼 라벨에도 반영: 배수유역 재산정 -> 분석 중… / 불러오는 중…
- 결과 문구에 출처와 소요를 붙인다: [저장분] ... / [재산정 28.4초] ...
- 저장분이 없으면 오류가 아니라 안내로 표시:
  "저장된 배수유역 분석이 없습니다. [배수유역 재산정]을 누르세요."
- 실패하면 버튼 활성 상태를 되돌려 껐다 켠 것처럼 보이지 않게 했다.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 21:26:32 +09:00
eomsangdonandClaude Fable 5 2ddf81c86c feat(B04/B05): 저장분 자동 표시 + 평균 흐름 화살표 + 갈래별 토글
1. 재산정하지 않는 한 저장분을 그대로 쓴다
   GET /drainage/primary-region 이 기본으로 영구저장소의 응답 캐시를
   그대로 돌려준다(0.0004s). refresh=true 일 때만 다시 계산한다.
   저장 배열에서 응답을 재조립하지 않고 **응답 자체**를 남긴다 —
   재조립하면 원본과 어긋날 여지가 생긴다.
   버튼 이름 배수유역 -> 배수유역 재산정. 지도를 열면 저장분이 자동으로 뜬다.

2. B05용 평균 흐름 화살표 (build_flow_arrows)
   셀 화살표는 1m 라 도면 배율에서 경향이 안 보인다. 겹치지 않는 10m 블록으로
   나눠 방향을 원형 평균하고, 40m 간격으로 솎아낸다.
   - 세류선 셀과 도로 셀은 뺀다. 그 자리는 확정된 물길/노면을 따르는 값이라
     사면 경향을 왜곡한다.
   - 산술평균이 아니라 원형 평균(0도와 359도의 평균은 180도가 아니다).
     평균 벡터 길이가 방향 일치도이므로 0.7 미만이면 그 블록은 버린다.
   - 블록 유효 셀이 절반 미만이면 건너뛴다(가장자리 조각 방지).
   실데이터 377개, 간격 40m 균일, 블록당 평균 98셀, 적 278 / 청 99.
   config: ARROW_BLOCK_M / ARROW_SPACING_M / ARROW_MIN_COVERAGE / ARROW_MIN_AGREEMENT

3. 갈래별 하이드/쇼 버튼 4종
   1차 유역 / 2차 유역 / 유역 방향 / 평균 흐름.
   평균 화살표는 B04 에도 올려 형태를 확인할 수 있게 했다.

4. B05 는 페이지 진입 즉시 유역도를 올린다
   B04 결과를 읽기만 해 0.1초면 끝나므로 버튼을 기다릴 이유가 없다.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 21:20:44 +09:00
eomsangdonandClaude Fable 5 79fe2fb7c9 feat(B04): 표본 화살표 + 오버레이 갈래별 토글 버튼
화살표가 안 보이던 문제
  1m 격자를 도엽 전체 배율로 보면 셀이 1~2px 다. 줌 상한이 8배라 셀이
  화살표 최소 크기(7px)에 절대 못 미쳐 채움색만 보였다.
  -> 셀마다 그리지 않고 화면상 약 22px 간격이 되도록 건너뛰며 표본만 그린다.
     화살표 크기는 그 간격에 맞춰 커진다. 흐름장을 읽는 표준 방식이다.
  -> 채움색 위에서도 읽히도록 흰 테두리를 한 겹 깔고 그 위에 그린다.

갈래별 토글 버튼 추가 (1차 유역 / 2차 유역 / 유역 방향)
  GIS 버튼 줄에 배수유역 전체 토글 옆으로 붙는다. 전체 토글이 꺼져 있으면
  갈래 설정과 무관하게 아무것도 그리지 않는다.
  그리는 순서는 유역방향(격자/화살표) -> 1차 유역 -> 2차 유역/관.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 21:09:20 +09:00
eomsangdonandClaude Fable 5 395e6b4b13 fix(B04): 배수유역 오버레이가 화면에 안 그려지던 문제
분석은 돌고 상태 문구도 나오는데 지도에는 아무것도 안 보였다.

원인: 프레임 함수 이름이 draw() 가 아니라 drawVectorLayer() 라
오버레이 draw 호출을 끼워 넣는 치환이 조용히 실패했다. 호출 자체가
소스에 없었다.

- drawVectorLayer() 안, GIS 레이어와 등고 라벨 뒤에 오버레이 draw 삽입.
  격자/화살표가 등고선을 덮어야 읽히므로 맨 위에 얹는다.
- drawPrimaryRegion 이 drawGridCells 를 한 번 더 부르던 중복 제거
  (draw 체인이 이미 먼저 호출한다).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 21:02:52 +09:00
eomsangdonandClaude Fable 5 8a0d640e0e refactor(B04/B05): 배수유역 분석을 B04로 이관, B05는 저장분 소비만
분석이 30초 걸리는데 B05는 일반 사용자 화면이다. 관리자 확인용 B04에서
한 번 돌려 저장하고, B05는 그 결과를 읽어 관 보충과 세부유역만 처리한다
(2026-07-31 사용자 지시).

노선 원천 변경
- B05 확정 경로 -> B03 업로드 계획 노선 파일(CSV). 분석이 노선 설계보다
  먼저 끝나 있어야 하기 때문. 샘플 planned_route_sample_epsg5187.csv 로 검증.
- common_util_route_geometry.py 신설 — RouteVertex/StructureCandidate/누가거리
  보간/세류 교차점/계획 노선 CSV 리더. B04와 B05가 같은 표현을 쓰도록 공용화.
  열 이름은 대소문자·한글 표기를 함께 받는다(B03이 여러 형식 수용 예정).

B04 (관리자 확인용, 신규)
- Engine_Watershed_{Grid,Stream,Descent,Flow,Expand,Export} — B05에서 git mv
- Engine_Watershed_Analyze.py — 1~8단계 오케스트레이션
- Router_Watershed.py — GET /drainage/primary-region
- UI_Watershed.ts — 2D 지도 GIS 레이어 그룹에 "배수유역" 토글 추가.
  격자/화살표/세류망/1차영역/2차유역/기본관을 겹쳐 그린다.
- 저장 위치 B05_wf2_Route/drainage -> B04_wf1_Surface/drainage
- 03_road_routing 단계 추가: B05가 세부유역을 나눌 최소 배열(셀->도로셀 귀속,
  유하장, 강도, 도로셀 제원, 셀 표고) + 계획도로선/기본배관/2차유역 기하

B05 (일반 사용자용, 축소)
- Engine_Drainage_Basin.py — B04 산출물 로더 + 관 보충(9) + 측구 라우팅/세부유역(10,11)
- Engine_Drainage.py 는 관경 산정만 남기고 322 -> 27줄
- Router_Drainage.py 509 -> 142줄. POST /drainage/basins 만 남김
- 화살표·격자·강도 띠 렌더 제거. 계획도로선/기본배관/2차유역만 받는다

삭제
- _legacy_watershed/ 4파일 (능선 행진 방식 원본 보관본)
- Engine_Watershed_Basin.py (B04 Analyze + B05 Drainage_Basin 으로 분할)
- GET /drainage/candidates 와 propose_structure_stations (구방식 후보 제안)

E2E 검증 (실데이터)
  B04 분석 28.2s -> 저장(geojson 11KB + npz 2.6MB)
  B05 로드 + 세부 설계 0.11s   <-- 30초가 0.1초로
  면적 457,404m2 로 B04 2차 유역과 정확히 일치
  관 편집 재산정 0.12s, 관 3개 -> 세부유역 3개, 면적 보존

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 20:57:09 +09:00
eomsangdonandClaude Fable 5 b0747e74b6 fix(B05): 6/7/8 단계 산출물도 영구저장소에 남긴다
확인해 보니 7(2차 유역 외곽선)과 8(기본 관)은 저장되고 있었으나
6(흐름 강도 곡선)이 빠져 있었고, 관에 누가거리가 안 붙었다.

- write_stage 의 레이어 항목이 (기하, 속성dict) 짝도 받도록 확장.
  관 피처에 chainage_m / reason / stream_name 이 실린다.
- 흐름 강도 곡선은 기하가 아니라 수치 곡선이라 GeoJSON 대신
  02_flow_direction.npz 에 strength_chainage_m / strength_area_m2 로 담는다.
- manifest 에 strength_points / strength_total_m2 요약 추가.

저장 확인 (실데이터)
  01_primary_region.geojson  688KB  kind: primary_region 1 / upstream 16 /
     downstream 860 / route 1 / grid_bbox 1 / basin_boundary 1 / pipe 1
     pipe 속성 = chainage_m 149.73, reason stream
  01_primary_region.npz       11KB  셀 마스크 + 확장 회차
  02_flow_direction.npz     2353KB  direction/reaches_road/analyzed/receiver/
     burned/band_elevation + 강도 곡선 71점(합계 457,404m2, 최대 150m)
  manifest.json                     3단계 요약 전부

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 20:04:26 +09:00
eomsangdonandClaude Fable 5 baef88dd50 feat(B05): 단계 검증에 흐름 강도·2차 유역 외곽선·기본 관 위치 추가
본 계산(POST /basins)은 손대지 않았다. 검증 경로에만 6/7/8 단계를 얹는다.

6. 흐름 강도
   색 판정은 세류 셀에서 멈춘다(거기서 도로 도달이 확정되므로). 강도는 그 물이
   세류를 타고 최종적으로 어느 도로 셀로 들어가는지를 봐야 하므로, 도로만
   흡수점으로 두고 한 번 더 따라가 도로 셀별로 센다.
   -> 누가거리 5m 구간별 유입 면적 곡선

7. 2차 전체 배수유역 외곽선
   적색 셀 전체를 폴리곤화한 외곽 링 + 면적

8. 기본 관 매설 위치
   도로 x 세류선 교차점. 20m 이내는 하나로 병합. 300m 보충 배치는 다음 단계.

프론트는 기존 렌더 경로를 그대로 쓴다 — 2차 유역선은 갈색 파선, 강도는 계획선
위 파란 띠, 관은 번호 마커. 버튼을 끄면 이 셋도 같이 걷는다.

실데이터 검증 (27.8s)
  적색 457,404셀 = 457,404m2
  7. 폴리곤 면적 457,374m2 (셀 면적의 100.0%)
  6. 강도 합계 457,404m2 (적색 면적의 100.0% - 귀속 누락 0)
     최대 지점 150m 에 409,244m2(89.5%) 집중
  8. 기본 관 1개 chainage 149.7m
     -> 강도 최대 지점과 정확히 일치. 세류가 도로를 건너는 그 자리다.
     교차 노드는 2개였으나 13.3m 간격이라 20m 병합 규칙으로 1개가 됐다.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 19:58:54 +09:00
eomsangdonandClaude Fable 5 2dcafcd286 fix(B05): 분석 요청 타임아웃 분리 + 확장 회차마다 방향장 재계산 제거
"signal is aborted without reason" 오류. 프론트 공통 타임아웃이 30초인데
등고선 하강 + 확장 루프가 33초 걸려 계산 도중 abort 됐다.

- config_frontend 에 API_ANALYSIS_TIMEOUT_MS(60초) 추가. 공통 30초는 그대로 두고
  분석 엔드포인트(primary-region, basins)에만 적용한다. 일반 요청까지 늘리면
  장애 시 화면이 오래 멈춘다.
- requestJson 에 timeoutMs 인자 추가. AbortError 원문은 원인을 알 수 없으므로
  "요청이 N초 안에 끝나지 않았습니다"로 바꿔 던진다.

같이 속도도 줄였다 (33.3s -> 26.2s, 결과 동일)
- 하강 방향장은 해석 영역과 무관하다 — 등고선 기하만으로 정해진다. 확장
  회차마다 다시 만들 이유가 없어, 격자가 커졌을 때만 새로 만들고 아니면
  재사용한다. 해석 영역은 마지막에 마스크로만 씌운다.
- 1차 영역 bbox 는 영역에 딱 붙어 있어 첫 회차부터 격자를 넓혀야 했다.
  시작할 때 사방에 확장폭 2배 여유를 둬 몇 회차는 격자를 안 넓히고 돈다.

검증: 적색 457,404 로 이전과 동일. 확장 3회 닫힘, 방향장 계산 4회 -> 2회.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 19:47:13 +09:00
eomsangdonandClaude Fable 5 44eb4ad2af feat(B05): 최외곽 적색 셀 주변 확장 루프 (Watershed_Expand 신설)
적색 셀이 해석 영역 최외곽에 있다는 것은 그 바깥에서 물이 더 들어온다는 뜻이다.
거기서 멈추면 유역이 잘린다. 반대로 최외곽이 전부 파랑이면 바깥 물은 도로로
오지 않으므로 더 볼 필요가 없다.

  1. 해석 영역 최외곽 셀 중 적색인 것을 찾는다
  2. 그 주변으로 한 겹(기본 50m) 넓힌다
  3. 넓힌 영역으로 흐름 방향/색을 다시 분석한다
  4. 새로 추가한 셀에 적색이 없으면 종료 (사용자 지시)
     최외곽에 적색이 아예 없어도 종료 - 둘 다 같은 판단이다

격자 bbox 에 닿으면 격자도 셀 정수배로 넓힌다. 도로 시작점 기준 격자점은
그대로 유지되므로 확장 전후 같은 자리 셀이 같은 자리에 남는다.

실데이터 결과 (33.3s)
  최외곽 적색  1,184 -> 400 -> 16 -> 0 (3회차에 닫힘)
  해석 셀      536,708 -> 640,872 (+104,164)
  적색         424,193 -> 457,404
  화살표=경로 일치 100%, 파랑인데 적색 가리킴 0개 (유지)

구조 변경
- Watershed_Expand.py 신설: analyze_domain(1회 분석) + expand_by_red_boundary(루프)
- StagePreview 에 spec/domain/expand_* 추가. 확장하면 격자가 1차 영역보다
  커지므로 화면/저장은 region.spec 이 아니라 이쪽을 봐야 한다.
- 라우터 응답에 expansion(회차/닫힘/추가 셀 수) 추가
- config: DRAINAGE_RED_EXPAND_BAND_M=50, DRAINAGE_RED_EXPAND_MAX_ROUNDS=20

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 19:36:38 +09:00
eomsangdonandClaude Fable 5 445f51bbcb feat(B05): 흐름 방향을 등고선 하강 방식으로 교체 (TIN 보간 폐기)
영구저장소 데이터를 오프라인 재현해 원인을 특정한 뒤 방식을 바꿨다.

진단 (실데이터 536,708셀, 저장본과 정확히 일치 재현)
- 파랑 셀 365,303개의 사슬 종료 사유가 100% 싱크. 영역 이탈 0%.
- 싱크 3,974개의 표고가 전부 5의 배수(600.0/765.0/585.0) = 등고선 값.
- 원인: 등고선 TIN 보간이 만든 평탄면을 EDT 로 해소할 때 가장 가까운
  비평탄 셀을 출구로 삼는데, 그게 오르막이면 물이 나갈 수 없어 싱크로 남는다.
  싱크 하나가 상류 유역 전체를 삼켰다(최대 10,171셀).
- 화살표(기울기 32방위)와 추적(D8)이 서로 달라 2,055셀에서 어긋났다.

교체 방식 — Watershed_Descent.py 신설
  1. 등고선을 격자에 직접 굽는다(보간면을 만들지 않는다)
  2. 셀마다 가장 가까운 등고 라인의 표고를 밴드로 삼는다
  3. 높은 밴드부터 내려오며 한 단 낮은 등고 라인까지 거리를 잰다
  4. 위치에너지 = 밴드 순위 x 큰 수 + 그 거리
  5. 수신 셀 = 위치에너지가 더 낮은 8이웃 중 화살표 방향에 가장 가까운 셀

위치에너지가 흐름을 따라 반드시 감소하므로 웅덩이도 순환도 원리적으로
생기지 않는다 — 채움/평탄해소 자체가 불필요해졌다. 수신 셀을 화살표에서
고르므로 화면 화살표와 실제 경로가 항상 일치한다.

실측 (같은 데이터)
              적색            파랑           싱크    화살표=경로
  TIN 방식    171,405 (32%)  365,303 (68%)  3,974   불일치 2,055셀
  등고선 하강 424,193 (79%)  112,515 (21%)  15,911  일치 100%, 불일치 0셀

부수 수정
- 최하단 밴드 셀을 무효가 아닌 정지 셀로 남겨 도로/세류면 적색으로 잡히게 함
- 저장에 receiver / band_elevation 추가 (사후에 사슬을 다시 따라갈 수 있게)
- manifest 에 row_spans 가 통째로 들어가 110KB 가 되던 것 정리

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 19:23:33 +09:00
eomsangdonandClaude Fable 5 12bee4bc82 fix(B05): 세류선 겹침 셀을 판정 맨 앞에서 적색 확정
세류선 위인데 빨강이 아닌 셀이 남던 문제. 원인 3가지를 함께 고쳤다.

1. 사슬의 마지막 셀 누락 — zip(chain, chain[1:]) 이라 각 갈래의 끝 셀이
   세류 셀로 표시되지 않았다. burned[chain] 전체로 바꿨다.
2. 표고 없는 셀 제외 — valid(등고선 TIN 껍질 안) 조건 때문에 껍질 밖
   세류 셀이 새김에서 빠져 회색으로 남았다. 세류선은 확정 자료이므로
   표고 유무를 따지지 않는다.
3. 사슬이 그런 셀로 흘러들면 not valid[following] 에서 파랑으로 판정됐다.
   이미 색이 정해진 셀이면 따라가도록 조건을 풀었다.

판정 순서를 지시대로 바꿨다:
  0. 세류선과 겹치는 셀을 먼저 적색으로 못박는다
  1. 최외곽 셀에서 사슬 추적
  2. 도로/세류 셀에 닿으면 사슬 전체 적색, 못 닿으면 전체 미도달
  3. 이미 색이 정해진 셀을 만나면 그 색을 물려받고 끝
  4. 최외곽에 안 걸린 내부 셀을 따로 출발

표고 없는 세류 셀도 화살표가 나오도록 descent_azimuth 에서 forced 셀을
유효로 인정. 합류 지점 끝 셀을 미연결로 오탐하던 경고도 갈래 공유
횟수로 판정하게 수정.

합성 검증(등고선 범위 밖으로 나가는 지류 포함): 세류 셀 806개 전부
적색·전부 판정·화살표 무효 0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 18:57:16 +09:00
eomsangdonandClaude Fable 5 638af3ecc6 feat(B05): 세류망 흐름 새김 + 32방위 화살표 + 확장 루프 분리
1. 세류선 위 셀이 파랑으로 나오던 문제
   원인: 등고선 TIN 보간면은 실제 물골(thalweg)을 재현하지 못해, 세류선 위
   셀인데도 D8이 옆 사면으로 흘려보내 도로에 닿지 못했다.
   조치 (2가지 함께):
   - burn_stream_flow: 확정된 상류 세류망을 따라 격자 흐름 방향을 강제로 새긴다.
     세류망은 이미 도로를 건너 하류로 빠지는 물길로 확정된 자료다.
   - 사슬 추적의 종결 조건에 세류망 셀을 추가. 물이 세류에 합류한 시점에
     도로 도달이 결정된다(사슬 끝이 도로 셀에 정확히 닿지 않아도 된다).
   - StreamSplit.upstream 을 물 흐름 방향(상류->하류)으로 정렬. 방향은
     _spread_network 확산 시 진입 끝점을 기록해 한 번에 정한다.

2. 화살표 8방위 -> 32방위
   descent_azimuth: 지표면 기울기에서 연속 최급강하 방위를 뽑아 32단계로 양자화.
   D8은 연결(도로 도달 판정)에만 쓰고 표시는 실제 지형 방위를 따른다.
   세류망 새김 셀은 확정된 물길 방향을 그대로 쓴다.
   응답 바이트: 하위 6비트=32방위(32=제자리, 33=표고없음), 0x80=도로 도달.

3. 확장 루프 분리
   Basin._solve_grid 안에 있던 루프를 Flow.expand_until_closed 로 분리.
   단계 검증 미리보기는 이 경로를 타지 않는다.

4. 도로 미도달 셀 화살표를 백색으로 변경(파랑 채움 + 백색 화살표).

합성 검증: 세류 새김 389셀 전부 도로 도달, 32방위 전부 등장,
지류가 본류 중간에 합류하는 갈래를 미연결로 오탐하던 경고 제거.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 18:50:21 +09:00
eomsangdonandClaude Fable 5 06f2328b55 feat(B05): 셀별 흐름 방향 판정 + 화살표/적청 표기 (확장 미포함)
최외곽 셀부터 물길을 따라가며 도로 도달 여부를 판정하고 화면에 셀 단위로
표기한다. 격자 확장은 다음 검증 단계라 넣지 않았다.

- classify_flow: 최외곽 셀에서 출발해 D8 수신 셀을 한 칸씩 따라가고, 경로가
  끝나면 결과를 경로 전체에 되돌려 적는다. 한 번 판정한 셀은 재분석하지 않고
  다른 경로가 만나면 즉시 결론을 가져온다. 최외곽으로 안 닿은 내부 셀은 그다음에
  따로 출발시킨다. 합성 검증 165,874셀 0.5s.
- outermost_cells: 해석 영역 밖에 8이웃이 닿는 셀 = 최외곽.
- build_terrain_grid(domain=): 1차 영역 셀 마스크 안쪽만 해석 대상으로 삼는다.
- direction_codes: 수신 셀 인덱스를 3x3 방향 코드로. 화살표 렌더용.
- 응답은 셀당 1바이트 base64 (하위4비트=방향, 15=표고없음, 0x80=도로도달).
  197,623셀 -> 257KB.
- 프론트: 셀마다 화살표. 도로 도달 적색, 미도달 파랑, 표고없음 회색.
  셀이 7px 미만이면 채움색만 남긴다. 격자선을 흰색으로 변경.
- 저장: 02_flow_direction.npz (direction/reaches_road/analyzed) + manifest

합성 검증(능선 y=200, 도로 y=500): 도달 셀 y 200~502, 미도달 y 150~600.
능선 남쪽과 도로 아래가 정확히 제외됨.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 18:23:35 +09:00
eomsangdonandClaude Fable 5 683a5c9198 feat(B05): 격자를 1차 영역 안에만 생성, 원점을 도로 시작점에 고정
bbox 사각형만 그리던 것을 실제 셀 생성으로 바꾼다.

- 격자 원점을 도로 시작점에 맞춘다(grid_spec_from_bounds anchor_xy).
  bbox 좌상단 기준이면 1차 영역이 조금만 달라져도 격자가 통째로 밀려
  이전 결과와 셀이 대응되지 않는다.
- build_cell_mask: 1차 영역에 조금이라도 걸치는 셀만 생성(all_touched).
  합성 검증에서 bbox 360,000셀 중 197,623셀(55%)만 생성.
- mask_row_spans: 셀을 행별 연속 구간으로 압축해 응답에 싣는다.
  낱개 대비 0.23% 크기(451구간 vs 197,623셀).
- expand_grid_spec 을 셀 정수배 확장으로 바꿔 확장해도 격자점이 유지된다.
- grid_transform 을 Flow -> Grid 로 옮겨 중복 제거.
- 프론트: bbox 전체 눈금 대신 생성된 셀만 흐린 선으로. 셀이 2px 미만이면
  구간을 통짜로 흐리게 칠하고 확대하면 셀 하나하나가 보인다.
- 저장: 01_primary_region_cells.npz 에 격자 규격 + 셀 마스크 기록
  (셀 수십만 개라 GeoJSON 폴리곤으로는 못 남긴다)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 18:05:58 +09:00
eomsangdonandClaude Fable 5 a77ee317a5 refactor(B05): 세류망·1차영역을 Watershed_Stream 모듈로 분리 (700줄 제한)
Grid.py 가 708줄로 제한을 넘어 관심사 단위로 갈랐다.

- Watershed_Stream.py (292줄): 세류망 노딩·도로 절단·연결망 확산,
  ElevationSampler, PrimaryRegion, build_primary_region
- Watershed_Grid.py (427줄): 등고선 구름, 격자 규격, TIN 보간,
  웅덩이 채움, 평탄면 해소, D8
- _iter_linestrings -> iter_linestrings, _spec_from_bounds ->
  grid_spec_from_bounds 로 공개(모듈 간 재사용). 의존 방향은 Stream -> Grid 단방향.
- 죽은 코드 제거: select_upstream_streams, _point_elevation

회귀 확인: 합성 유역 179,919㎡ 동일, 연결망 판정 동일.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 17:57:13 +09:00
eomsangdonandClaude Fable 5 578ada6d84 feat(B05): 1차 영역 반경 100m + 노선 버퍼 복귀, 단계별 산출물 저장 모듈 분리
- DRAINAGE_INITIAL_RADIUS_M 50m -> 100m
- 1차 영역 = 상류 세류망 buffer(100m) UNION 계획 노선 buffer(100m).
  세류망 선정이 정확해져 노선 버퍼를 다시 넣어도 영역이 폭발하지 않는다.
- 저장을 B05_wf2_Route_Engine_Watershed_Export.py 로 분리.
  STAGES 딕셔너리에 단계 이름을 추가하고 write_stage 를 부르면
  drainage/{번호}_{단계}.geojson + manifest.json 이 함께 갱신된다.
  앞으로 기능을 붙일 때마다 이 자리에 단계가 하나씩 쌓인다.
- primary_region 단계에 route 레이어 추가(도로 대조용).
- 라우터의 임시 GeoJSON 작성 코드 제거.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 17:53:43 +09:00
eomsangdonandClaude Fable 5 e1b410c391 feat(B05): 1차 배수유역을 상류 세류망 기준으로 재정의 + 단계 검증 화면
1차 영역이 도로 전체 버퍼까지 포함해 도로 아래(하류)로 퍼지고, 세류선을 피처
단위로만 갈라 상류망이 통째로 누락되던 문제를 고친다.

- 1차 영역 = 도로 교차점 상류로 이어진 세류망만 반경 버퍼. 노선은 버퍼하지 않음.
  노선이 영역 밖으로 나간 길이(road_outside_m)를 재서 반경 판단 근거로 노출.
- 상류 판정을 연결망 기준으로 교체: unary_union 노딩 -> 도로에서 절단 ->
  끝점 그래프 -> 도로 교차 노드를 통과하지 않는 확산. T자로 붙은 지류와
  2단계 이상 이어진 지류까지 상류망으로 따라간다.
- 상하류 판정 표고를 최근접 등고선 정점에서 TIN 선형보간으로 교체
  (ElevationSampler). 최근접 정점은 오차가 등고선 간격만큼 나서 계곡 교차점이
  한 등고선 위로 잡히고 상류 조각이 전부 하류로 오판됐다.
- 격자 크기 자동 강등 삭제 - config 값을 그대로 쓴다. 셀 수가 많으면 경고만.
  자동 강등이 도로 굽기 두께 전제를 조용히 깨뜨렸다.
- TIN 삼각망을 격자 범위 + 여유로 클리핑. 결과 동일, 속도만 개선.
- 기본 반경 300m -> 50m (단계 검증용).

단계 검증 수단
- GET /drainage/primary-region : TIN/흐름 계산 없이 상류망/하류망/1차영역/격자만 반환
- 프론트 1차영역 버튼 : 상류망(굵은 파랑), 하류망(회색 파선), 1차 영역(초록 채움),
  해석 격자를 실제 셀 눈금으로 렌더. 켤 때마다 재요청한다.
- 클릭 시 storage/{project}/B05_wf2_Route/drainage/primary_region.geojson 저장

합성 검증(T자 지류 + 2단계 지류 + 하류 지류 + 고아 세류):
상류망 4조각 채택 / 하류망 3조각 / 미연결 1개 제외 - 전부 기대대로.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 17:44:53 +09:00
eomsangdonandClaude Fable 5 c1f139527d feat(B05): 배수유역을 격자 흐름 해석으로 전면 재설계
등고선 아크 추적 + 능선 행진 방식이 능선/계곡을 안정적으로 분리하지 못해
D8 물 방향 + 도로 기준 상류 추적 방식으로 교체한다.

- 능선을 따로 탐지하지 않는다. 물길을 따라가 도로에 닿는 셀만 유역이고,
  그 경계가 곧 능선이다. 유역 내부 봉우리는 자동으로 포함된다.
- 등고선 TIN 보간 후 웅덩이 채움(형태학적 재구성) + 평탄면 미세경사로
  가짜 웅덩이/평탄 삼각형에서 흐름이 끊기는 문제를 없앤다.
- 상류 추적은 포인터 더블링으로 전 셀을 한 번에 푼다. 셀의 흐름 종착
  도로 셀(root)이 유역 판정·흐름 강도·세부유역 라벨의 공통 근거가 되어,
  관을 옮겨도 격자 해석 없이 측구 라우팅만 다시 돌면 된다(.npz 캐시).
- 활성 셀이 격자 최외곽에 닿은 방향으로만 확장하고, 경계 링이 전부
  비활성이 되면(띠 폐합) 멈춘다.

변경 사항
- 신규 엔진 3종: Engine_Watershed_Grid / _Flow / _Basin
- 폐기 엔진 4종은 _legacy_watershed/ 로 원본 보관(ruff 제외)
- config_system.py §5-3-1 에 DRAINAGE_* 파라미터 18개 (격자 1m, 반경 300m)
- 표고점 데이터 사용 중단(유효 데이터 부족), 프론트 능선 토글 제거
  (전체 유역 외곽선과 같은 선이므로 중복)
- 응답에 main_polygon_lonlat / strength_profile 추가, 계획선 위 흐름 강도 표기

합성 지형 검증: 유역 179,919㎡ vs 이론 180,000㎡ (오차 0.04%),
능선 자동 검출, 확장 3회 후 자동 정지, 캐시 재사용 1.2s -> 0.1s

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 16:51:47 +09:00
eomsangdon 9c8a2f2b17 ㅇㅈ 2026-07-30 18:03:35 +09:00
eomsangdon 12f1666506 auto: 2026-07-30 17:25 (EOMSANGDON-HOME) 2026-07-30 17:25:13 +09:00
eomsangdon f72017a6ee auto: 2026-07-29 20:06 (EOMSANGDON-HOME) 2026-07-29 20:06:57 +09:00
eomsangdon 4fbaf63a60 auto: 2026-07-29 20:00 (EOMSANGDON-HOME) 2026-07-29 20:00:50 +09:00
eomsangdon 077da60065 auto: 2026-07-29 19:41 (EOMSANGDON-HOME) 2026-07-29 19:41:58 +09:00
eomsangdon 5354e72258 auto: 2026-07-29 19:31 (EOMSANGDON-HOME) 2026-07-29 19:31:38 +09:00
eomsangdon d5ba1f1eb5 auto: 2026-07-29 19:23 (EOMSANGDON-HOME) 2026-07-29 19:23:04 +09:00
eomsangdon afa04d8c23 auto: 2026-07-29 19:13 (EOMSANGDON-HOME) 2026-07-29 19:13:27 +09:00
eomsangdon 07cc96ea37 auto: 2026-07-29 18:44 (EOMSANGDON-HOME) 2026-07-29 18:44:53 +09:00
eomsangdon 067de3a25f auto: 2026-07-29 18:40 (EOMSANGDON-HOME) 2026-07-29 18:40:45 +09:00
eomsangdon 368764190f auto: 2026-07-29 18:23 (EOMSANGDON-HOME) 2026-07-29 18:23:23 +09:00
eomsangdon 8b853e8dec auto: 2026-07-29 18:07 (EOMSANGDON-HOME) 2026-07-29 18:07:39 +09:00
eomsangdon 121d20ef80 auto: 2026-07-28 20:03 (EOMSANGDON-HOME) 2026-07-28 20:03:32 +09:00
eomsangdon cb35e8c1cf auto: 2026-07-28 19:53 (EOMSANGDON-HOME) 2026-07-28 19:53:22 +09:00
eomsangdon fa5e08c2ce auto: 2026-07-28 19:36 (EOMSANGDON-HOME) 2026-07-28 19:36:27 +09:00
eomsangdon 7f5c0ab069 auto: 2026-07-28 19:32 (EOMSANGDON-HOME) 2026-07-28 19:32:17 +09:00
eomsangdon 7a7cd217b6 auto: 2026-07-28 19:23 (EOMSANGDON-HOME) 2026-07-28 19:23:57 +09:00
eomsangdon 294151f862 auto: 2026-07-28 19:19 (EOMSANGDON-HOME) 2026-07-28 19:19:31 +09:00
eomsangdon 86d27add43 auto: 2026-07-28 18:33 (EOMSANGDON-HOME) 2026-07-28 18:33:47 +09:00
eomsangdon 0c51aef49e auto: 2026-07-28 18:28 (EOMSANGDON-HOME) 2026-07-28 18:28:02 +09:00