Files
Aislo/docs/raw/plans/2026-07-17_plan_b04_defects.md
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

10 KiB

PLAN: B04 결함 수정 6건 + 확정 흐름 주의점 보완 (2026-07-17)

활성 작업: B04 결함 수정 6건 + 확정 흐름 주의점 보완 (2026-07-17)

A. 지면 필터 미리보기가 원본 데이터를 표시 (핵심 결함)

  • 현상: 지면 필터를 선택해도 필터링 후 데이터가 아닌 원본에서 50만 개를 샘플해 표시
  • 원인 (확인됨):
    • GET .../surface/point-cloudstructured.npz(지면 필터 이전 구조화 원본)를 고정 조회 — B04_wf1_Surface_Router.py:332. 필터 파라미터 자체가 없음
    • 지면 필터 마스크는 분석 중 메모리에서만 생성·사용되고 영구저장되지 않음B04_wf1_Surface_Engine.py:77-85 (build_ground_masks → 모델 빌드에 전달 후 소멸)
  • 0_old 검증 프로그램의 방식 (준용): create_ground_filter_cache(0_old/backend/app/analyzer.py:622-739)가 분석 시 필터링된 지면 포인트 자체를 브라우저 상한(0_old는 500만)으로 샘플해 processed/ground-points.json으로 영구저장하고, GET .../ground-points(0_old/backend/app/main.py:166-170)가 캐시 파일을 그대로 서빙. 요청 시 재계산 없음
  • 수정 방향 (0_old 패턴 준용):
    1. Engine: 분석 시 필터별 지면 포인트 캐시를 processed/ground_points_{filter}.npz로 영구저장 — 50만 규칙(초과 시 랜덤 50만, 이하 전체) 적용된 미리보기 서빙용. 메타(원본 지면 포인트 수, 샘플 수, bounds) 포함
    2. Router: point-cloud 엔드포인트에 filter 쿼리 파라미터 추가 — 지정 시 해당 캐시 파일 서빙, 미지정 시 기존 원본(structured) 동작 유지
    3. Api_Fetch / UI_Page: 지면 필터 선택 변경 시 filter 파라미터로 재조회 → 뷰어 렌더
    4. 기존 프로젝트에는 캐시 파일이 없음 → filter 요청 시 파일 부재면 structured.npz로 해당 필터만 온디맨드 재계산 후 캐시 저장(백필)
    5. 경로(B05)·등고선 등 백엔드 분석은 종전대로 전체 필터링 데이터 기준(미리보기 캐시와 무관) 유지

B. 등고선 간격 옵션 미작동

  • 원인 (확인됨): 등고선은 분석 시 config 기본 간격(contour_interval_meters=5.0m) 파일만 사전 생성 — B04_wf1_Surface_Engine_Pipeline.py:57-104. contour 엔드포인트는 파일 조회 전용이라 다른 간격 요청 시 파일이 없고, 폴백 glob이 기존 5.0m 파일을 반환 — B04_wf1_Surface_Router.py:655-662간격 변경이 조용히 무시됨. 프론트 전달(interval 파라미터)은 정상 — B04_wf1_Surface_UI_TerrainViewer.ts:383-385
  • 수정 방향: 요청 간격의 파일이 없으면 저장된 모델 npz로 온디맨드 등고선 생성 후 파일 캐시 저장(기존 Engine_Contour 함수 재사용, asyncio.to_thread). 폴백 glob은 생성 실패 시에만 사용하거나 제거

C. 스무딩 옵션 동작 불명확

  • 확인된 사실: 프론트 연결은 정상(smooth 파라미터 전달 및 토글 시 재로드 — UI_TerrainViewer.ts:326-329,568-570). 스무딩은 tin/dtm 전용이며 {stem}_smooth_preview.glb 사전 생성 파일을 조회(Router.py:567-570). nurbs/implicit/meshfree는 토글해도 무동작이 현재 설계
  • 수정 방향:
    1. 실행 검증: tin/dtm에서 _smooth_preview.glb·contour_*_smooth_*.json 파일 실존 및 토글 시 메시 교체 여부 확인, 파일 누락 시 Pipeline 생성 경로 수정
    2. UX: 스무딩 미지원 기법(nurbs/implicit/meshfree) 선택 시 토글 비활성화(회색)로 "안 되는 게 아니라 해당 없음"을 명시
    3. 등고선도 스무딩 상태와 간격을 함께 반영(B와 연동)

D. 2D 지도 이미지 왜곡 (그룹 컨테이너에 맞춰 늘어남)

  • 원인 (확인됨): .b04-map__image { object-fit: fill; width/height:100% }B04_wf1_Surface_UI_Style.css:468-478. VWorld 배경 이미지가 뷰포트(가변 폭 × 고정 560px)에 종횡비 무시로 강제 인장됨. 이전에 보고된 "살짝 대각선" 현상도 이 왜곡이 원인일 가능성 높음
  • 수정 방향: VWorld 요청 해상도를 뷰포트 실제 크기(종횡비)에 맞춰 요청하거나 object-fit 정합 처리 + canvas GeoJSON 오버레이 좌표 변환 동기화. 수정 후에도 대각선 느낌이 남으면 투영 좌표 변환 재점검

F. 두 3D 뷰어 카메라 동기화의 스케일 불일치

  • 현상: 포인트클라우드 미리보기 ↔ 지표면 표현 뷰어가 함께 움직이긴 하나 배율(스케일)이 서로 다름
  • 원인 (확인됨): 카메라 상태를 비율로 주고받는데 정규화 기준이 서로 다름
    • Viewer: 데이터를 최대 span=180 고정 공간으로 스케일링(UI_Viewer.ts:202)하고 distanceRatio = distance/180 고정 기준(UI_Viewer.ts:165-187)
    • TerrainViewer: 실좌표 메시의 바운딩박스 span 기준 distance/sceneSpan(UI_TerrainViewer.ts:270-296)
    • 포인트클라우드 범위(현재는 원본이라 식생 포함 z-range 큼)와 지표면 메시 범위가 달라 같은 비율이 다른 배율로 나타남. A 수정 후에도 기준 데이터 범위가 완전히 같지 않으므로 잔존
  • 수정 방향: 카메라 상태를 실세계 좌표(m) 기준 공통 프레임으로 교환 — 각 뷰어가 자기 월드→씬 변환(Viewer: 180/span 배율+중심점, Terrain: sceneCenter)을 알고 있으므로, emit/apply 시 실좌표(중심 기준 오프셋 m, 거리 m)로 변환해 동기화. 축 방향 컨벤션(Viewer의 x/z/-y 스왑)도 함께 정합

G. 지표면 표현 뷰어가 컨테이너 전체를 사용하지 못함

  • 원인 (확인됨): TerrainViewer 렌더 영역에 인라인 height: 520px 고정(UI_TerrainViewer.ts:106) — 포인트클라우드 뷰어(.three-viewer 560px, UI_Style.css:355-360)와 불일치하고, 2열 컨테이너 높이와 무관하게 고정됨
  • 수정 방향: 인라인 높이 제거, 두 뷰어 동일한 CSS 규칙(컨테이너 기준 100% 또는 공통 고정값)으로 통일. ResizeObserver 기반 리사이즈는 이미 구현되어 있어(UI_TerrainViewer.ts:587-596) CSS만 정리하면 추종함

E. 확정 흐름 주의점 보완 (2026-07-17 검증에서 발견)

  1. enableRouteStep이 진행단계 버튼을 DOM 인덱스 [2] 하드코딩으로 탐색(UI_Page.ts:186-188) → 스텝 식별자/route 기반 탐색으로 변경 (스텝 순서 변경 시 무음 파손 방지)
  2. 확정 토스트의 {smoothing} 값이 "-" 고정(UI_Page.ts:304) → 실제 스무딩 토글 상태 연결
  3. B04 재진입 시(이미 확정 상태) WF2 스텝 활성화가 서버 workflowState 렌더에 의존 → 확정 → 새로고침 → 스텝 클릭 가능 여부 실행 검증, 불가하면 수정
  4. 잔여 확인: 지면 필터 드롭다운의 ransac 제거(UI_Page.ts:54 SOURCE_FILTERS) — 이전 계획 항목이나 현재 코드에 잔존
  5. 확정 성공 시 B05로 자동 이동 (사용자 확정, 2026-07-17): 확정 토스트 표시 후 goToWorkflowStage로 WF2(B05) 페이지 자동 전환. 스텝 수동 클릭 대기 방식 폐기 — 단, enableRouteStep의 스텝 활성화 처리는 뒤로가기/재진입 대비 유지

대상 파일

파일 작업
B04_wf1_Surface/B04_wf1_Surface_Engine.py 필터별 지면 포인트 캐시 영구저장 (A)
B04_wf1_Surface/B04_wf1_Surface_Router.py point-cloud filter 파라미터+캐시 서빙 (A), 등고선 온디맨드 생성 (B)
B04_wf1_Surface/B04_wf1_Surface_Api_Fetch.ts point-cloud filter 파라미터 전달 (A)
B04_wf1_Surface/B04_wf1_Surface_UI_Page.ts 필터 선택 → 재조회 연결 (A), E-1·2·4
B04_wf1_Surface/B04_wf1_Surface_UI_Viewer.ts 카메라 상태 실좌표 변환 (F)
B04_wf1_Surface/B04_wf1_Surface_UI_TerrainViewer.ts 스무딩 토글 UX (C), 카메라 상태 실좌표 변환 (F), 인라인 높이 제거 (G)
B04_wf1_Surface/B04_wf1_Surface_UI_MapViewer.ts 이미지 종횡비 정합 (D)
B04_wf1_Surface/B04_wf1_Surface_UI_Style.css object-fit 수정 (D), 뷰어 높이 통일 (G)
B04_wf1_Surface/B04_wf1_Surface_Engine_Pipeline.py (조건부) 스무딩 프리뷰 생성 누락 시 수정 (C)

구현 체크리스트

  • A-1. Engine: 분석 시 필터별 processed/ground_points_{filter}.npz 캐시 저장 (50만 규칙 + 메타)
  • A-2. Router: point-cloud filter 파라미터 + 캐시 파일 서빙
  • A-3. Router: 캐시 파일 부재 시 온디맨드 재계산·백필
  • A-4. Api_Fetch/UI_Page: 필터 선택 → filter 파라미터 재조회·렌더
  • B-1. Router: 요청 간격 등고선 파일 부재 시 온디맨드 생성 + 캐시 저장
  • B-2. 폴백 glob 정리 (간격 무시 무음 동작 제거)
  • C-1. tin/dtm 스무딩 프리뷰·등고선 파일 생성 여부 검증, 누락 시 Pipeline 수정
  • C-2. 스무딩 미지원 기법에서 토글 비활성화
  • D-1. VWorld 이미지 종횡비 정합 + canvas 오버레이 좌표 동기화
  • F-1. Viewer/TerrainViewer: 카메라 상태를 실세계 좌표(m) 공통 프레임으로 emit/apply 변환 (축 컨벤션 정합 포함)
  • F-2. 동기화 배율 일치 실행 검증 (양쪽 축척바 값 비교)
  • G-1. TerrainViewer 인라인 높이 제거, 두 뷰어 높이 통일
  • E-1. enableRouteStep 스텝 탐색 방식 개선
  • E-2. 확정 토스트 스무딩 값 연결
  • E-3. 확정 후 새로고침 시 WF2 스텝 활성화 실행 검증
  • E-4. SOURCE_FILTERS에서 ransac 제거 확인
  • E-5. 확정 성공 시 B05 자동 이동 (goToWorkflowStage)
  • 700줄 제한 및 라우터 분리: B04_wf1_Surface_Router.py(643줄), B04_wf1_Surface_Router_GIS.py(175줄)로 분할하여 700줄 한계 준수
  • GIS/VWorld 라우터 복원: B04_wf1_Surface_Router_GIS.py 유실 복구 및 main.py 기동 실패(ImportError) 해결

협의 완료 (2026-07-17 사용자 확정)

  • 확정 후 자동 이동: 확정 성공 시 B05 자동 전환으로 확정 → E-5 반영
  • A-3 백필 방식: 온디맨드 재계산 채택 — 기존 프로젝트(캐시 파일 없음)에서 필터 첫 선택 시 서버가 structured.npz로 해당 필터만 재계산해 캐시 생성 후 응답. 첫 선택에만 수 초 대기 발생, 이후 즉시 표시