Files
GhiVideo/docs/DefVideo→GhiVideo_업그레이드_상세.md
T
b23042andClaude Opus 4.8 e0ff5dd6d0 feat: 지도 나침반(OSM/위성) 추가 + 측점 검색·마커 정확도 개선 + POI/영상 수정
- 지도 기반 나침반(노스업, 현재위치, 시야 역삼각형): 호버 확대·휠 줌·클릭 위성전환, OSM/Esri 타일 서버 프록시(/api/tile)
- 스테이션 검색: 실제 측점(chain) 기준 이동, 없으면 '측점 없음' 안내
- 역 마커: 직교 투영 측점 일치 시에만 표시, 미도착 종점은 추가 방식
- POI 팝업 겹침/재등장·라벨 정합 수정
- 영상 fps 데이터 기반 자동 산출
- 기술/발표/쉬운설명 문서 추가

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

254 lines
16 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.
# DefVideo → GhiVideo 업그레이드 상세 문서
> 작성일: 2026-06-30
> 비교 기준: `b23042/DefVideo`(구버전) → `b23042/GhiVideo`(신버전)
> 두 저장소는 git 히스토리가 분리된 별도 저장소이며, 동일 코드베이스에서 파생되었다. 본 문서는 실제 소스 트리(`diff -r`)를 직접 비교하여 작성되었다.
---
## 0. 한눈에 보기
| 영역 | 변경 규모 | 평가 |
|------|----------|------|
| **서버 (server)** | `app.ts` +2줄, 신규 `elevation.ts`(69줄) | 거의 동일 — 골격 유지 |
| **공유 타입 (shared)** | 변경 없음 | 동일 |
| **클라이언트 (client)** | 약 **+3,000줄** 순증 | **대폭 발전** |
**핵심 결론**: 기반 골격(서버 스트리밍/HLS/업로드, 공유 타입)은 그대로 유지하면서, **측점(체이니지) 기반 주행영상 분석 도메인 기능**이 본격적으로 구현된 한 단계 발전한 버전이다. 세 갈래로 요약하면:
1. **V2.0 데이터 형식 대응** — 영상 옆 KMZ/KML을 1순위로 POI·구조물 추출(없으면 `building/` 5종 CSV 폴백), 측점 CSV 기반 중심선 생성, 자동 인코딩 감지, 방향전환점 추출
2. **투영 정확도 개선** — 지오이드 보정, 지면고도(DEM) 적용, 역투영 기반 위치 보정
3. **영상 오버레이/편집 UI 고도화** — 라벨 평활, 겹침 억제, 컴팩트 팝업, 드래그 편집, 나침반 미니맵
---
## 1. 파일 단위 변경 요약
### 변경된 기존 파일
| 파일 | 변경량 | 내용 |
|------|--------|------|
| `client/src/components/overlay/StationOverlay.tsx` | **+1,188 / 167** | 라벨 평활·겹침억제·구조물표출·컴팩트팝업·편집모드 (최대 변경) |
| `client/src/utils/geoData.ts` | **+660 / 111** | V2.0 폴더 파싱·구조물·방향전환점·POI보정 |
| `client/src/stationbar/StationBar.tsx` | +340 / −87 | 이동거리축·시설등급필터·종점역 미도착 |
| `client/src/stationbar/components/Timeline/Timeline.tsx` | +248 / −27 | 구조물 겹침필터·라벨 그룹핑 |
| `client/src/components/player/VideoPlayer.tsx` | +168 / 40 | 영상컨트롤 UI 통합·smoothTimeRef·폴더 드롭 |
| `client/src/utils/geoProjection.ts` | +138 / −5 | 지오이드 보정·역투영 함수 3종 |
| `client/src/types/geo.ts` | +54 / −7 | 구조물/방향전환점/보정 타입 |
| `client/src/store/geoStore.ts` | +47 / −3 | POI 보정 분리관리·구조물 상태 |
| `client/src/components/overlay/RoutePanel.tsx` | +27 / −5 | 구조물 필터·방향 우선순위 |
| `client/src/store/playerStore.ts` | +13 / 1 | videoReady·영상 해상도 상태 |
| `client/src/hooks/useVideoPlayer.ts` | +8 / −0 | 영상 로드 이벤트 추적 |
| `server/src/app.ts` | +2 / 0 | elevation 라우트 등록 |
### 신규 추가 파일 (DefVideo엔 없음)
| 파일 | 줄수 | 역할 |
|------|------|------|
| `client/src/components/overlay/Minimap.tsx` | 69 | 드론 방위각(heading-up) 나침반 미니맵 |
| `client/src/store/settingsStore.ts` | 90 | 사용자 표시 설정 중앙 관리(localStorage 영속) |
| `client/src/utils/chainage.ts` | 64 | 측점값(체이니지) 계산·중심선 투영 |
| `server/src/routes/elevation.ts` | 69 | DEM(표고) 데이터 프록시 API |
---
## 2. 데이터 처리 — V2.0 형식 대응 (`geoData.ts`)
가장 많이 변경된 유틸. 구버전은 `center.csv` + 단일 POI CSV 중심이었으나, 신버전은 **영상 옆(root)의 KMZ/KML을 1순위 소스**로 POI·구조물을 직접 추출하고, `building/` 하위 CSV는 **KMZ가 없을 때의 폴백**으로 둔다(`loadFolderGeoData``useKmz` 분기). 측점만 KMZ에 없어 항상 CSV에서 읽되, 측점·역사 CSV는 `building/` 또는 root 어디서든 찾는다(폴더 삭제 대비). 즉 현재 데이터는 KMZ(root) + 측점 CSV(root)만으로 동작하며 building 폴더가 필수는 아니다.
### 2.1 자동 인코딩 감지
- **구버전**: 파일별로 `utf-8` / `euc-kr`를 코드에 하드코딩
- **신버전**: ArrayBuffer 앞 3바이트(BOM `EF BB BF`)를 검사해 UTF-8/EUC-KR 자동 선택 → 혼재된 인코딩 자동 처리
### 2.2 데이터 소스 우선순위 (KMZ 우선 · CSV 폴백)
```
1순위: 영상 옆(root) *.kml / *.kmz → parseKmz → POI·구조물 직접 추출
(KMZ에 POI/구조물 있으면 useKmz=true → 아래 building CSV 미사용)
측점: 01)측점.csv (building/ 또는 root) → 측점 + 방향전환점 (KMZ에 없어 항상 CSV)
역사: 02)지장물_역사.csv (building/ 또는 root) → 스테이션바 전용
폴백(KMZ 없을 때만):
building/02)지장물.csv → POI(지장물)
building/03)교량.csv → 구조물(bridge)
building/04)터널.csv → 구조물(tunnel)
building/05)출입문번호.csv → POI(출입문)
building/06)구교.csv → 구조물(bridge, 구교 아이콘)
```
- `center.csv`를 더 이상 찾지 않고, **측점(01)을 측점값 순으로 이어 중심선을 생성**(`buildCenterlineFromStations()`)
- 지장물·교량·터널·구교·출입문 CSV는 `building/` 안에서만 찾지만, KMZ가 있으면 통째로 무시되므로 KMZ 기반 데이터에서는 building 폴더가 없어도 정상 동작한다.
### 2.3 CSV 파싱 견고성 강화
- `makeFieldIndexer()`: 헤더명 기반 인덱싱 실패 시 위치 인덱스로 폴백 → 깨진 EUC-KR 헤더도 안전
- `cell()`: 음수/범위 초과 인덱스에서 빈 문자열 반환 → 크래시 방지
- `rowProps()`: CSV 원본 속성을 `{k, v}[]` 쌍으로 추출 → UI 라벨 클릭 팝업에 그대로 표출
### 2.4 신규 파싱 항목
- **측점 + 방향전환점**(`parseStations()`): `Z좌표_한국`(정표고, EPSG:5186) 우선 사용, 비고 컬럼에서 `방향전환점(상행->하행, 02:05)` 정규식 추출 → `DirectionChange` 생성
- **구조물**(`parseStructures()`): 교량/터널/구교 3종 통합, 위경도 대한민국 범위(33–39°N, 124132°E) 검증, `연장(m)·시설종별·분류` 채움
- **역사**: `type='station'`으로 변환 → 스테이션바에만 표시(영상 오버레이 제외)
- **KMZ 원본**(`parseKmz()`): 구글 어스 KMZ가 있으면 직접 추출(CSV 중복 회피), 측점만 항상 CSV 사용
- **POI 위치 보정**(`parsePoiOverrides()`/`applyPoiOverrides()`): 드래그 보정값을 `<base>_poi_overrides.json`(`{title: {lat,lon,z}}`)으로 저장/복원
---
## 3. 투영 정확도 개선 (`geoProjection.ts`, `chainage.ts`)
### 3.1 카메라 파라미터 신규 필드
- `geoidOffset`(m): 정표고(EL) → 타원체고 변환. 드론 `abs_alt`(타원체고)와 datum 일치 (대전 기본 25.8m, KNGeoid18)
- `poiZOffset`(m): POI 전용 표고 보정. 선로 지면 가정 오차를 패널에서 조절
### 3.2 카메라 좌표 신규 필드
- `distH`: 수평 거리(m) — POI 거리필터용
- `fwd`/`side`: 진행방향(yaw) 기준 앞쪽/옆쪽 거리(m) → **비등방 거리필터**(앞은 멀리, 옆은 가깝게) 구현 가능
### 3.3 역투영 함수 3종 신규 (편집 기능의 수학적 기반)
- `worldFromPixel(px, py, range)`: 화면 픽셀 + 슬랜트거리 → 월드좌표(lat/lon/z). POI 드래그 보정(수평+수직 동시)
- `solveZForPixelY(...)`: 특정 화면 세로위치에 맞는 표고(z) 역산. "앞으로 밀려 보이는 슬라이드" 보정
- `groundPointFromPixel(px, py, zGround)`: 화면점과 특정 고도평면의 교차점(lat/lon). 지면 좌표 보정
### 3.4 측점/체이니지 계산 (`chainage.ts`, 신규)
- `kmFromTitle("157K970")``157970`(m), `fmtKm10()``"157k970"`
- `buildChainLine()`: 측점 → 평면투영 폴리라인
- `projectToChain()`: 드론 GPS → 폴리라인 투영 → `{km(측점값), offsetM(선로 수직이격)}`
- `nearestChainPoint()`: 최근접 측점 검색 → StationBar·VideoPlayer HUD 공유
---
## 4. 영상 오버레이 고도화 (`StationOverlay.tsx`)
### 4.1 라벨 평활 & 이상치 거부
- **이상치 거부**: 한 프레임에 화면폭 12%(`REJECT_DIST`) 이상 점프하면 노이즈로 보고 이전 위치 유지. 단 연속 8프레임(`MAX_REJECT_FRAMES`) 초과 시 수용 → 시크/재등장에서 멈추지 않음
- **속도 적응형 평활(One Euro 방식)**: 떨림(방향 왕복)은 강하게 평활, 실제 이동(방향 일관)은 즉시 추종 → 1배속 떨림 제거 + 빠른 배속 지연 해소
- 조절 파라미터: `smoothMinAlpha`(정지 시 최소 추종), `smoothSpeedRef`(즉시추종 기준속도)
### 4.2 POI 겹침 억제
- 화면상 가로 10%·세로 3.5% 이내 마커는 겹침으로 판정 → 드론에 더 가까운 것만 표시
- 같은 건물 여러 업체/시설 중 주요한 것만 노출 → 화면 정리
### 4.3 구조물 표출 & 컴팩트 팝업
- 교량/터널/구교를 POI처럼 라벨로 표출(이모지 🌉 구교, 🚪 출입문 추가)
- **컴팩트 필드**: 라벨 옆 항상 표시(시설종별 → 구조형식 → 연장 → 폭 → 용도 → 준공연도 순)
- 클릭 시 전체 팝업 확장, 다시 클릭 시 컴팩트로 토글. **다중 팝업** 누적 가능, ESC로 정리
### 4.4 POI 높이 모드 & DEM 자동 적용
- **드론 높이 기준**: POI를 드론보다 N미터 아래 고정(`droneHeightDrop`)
- **지면고도 기준**: POI를 실제 지형고도에 고정 → 드론 오르내려도 지물에 붙음
- 서버 elevation API(SRTM 30m DEM)로 모든 좌표의 실제 표고 일괄 적용 → 먼 곳에서 밀리는 현상 완화
### 4.5 드래그 편집 & 세로화각 보정
- POI를 마우스로 드래그 → `groundPointFromPixel`로 위경도 역산
- 표고 슬라이더로 높이 미세조정, 개별 리셋 가능
- **세로화각 보정 모드**: 라벨이 상하로 어긋날 때 POI를 실제 위치로 드래그 → 세로 화각(sensorH)만 역산 자동보정
- 보정값 JSON 내보내기/가져오기, 일괄 초기화
### 4.6 좌표 정렬 (object-fit:cover 보정)
- `coverRef`: 영상이 `object-fit:cover`로 크롭된 영역을 매 프레임 계산 → 정규좌표(0–1) ↔ 화면 px 정확 변환 → 포인터 히트테스트 정확
---
## 5. 나침반 미니맵 (`Minimap.tsx`, 신규)
- 우측 상단 고정 150×150px 아날로그 나침반 위젯(heading-up)
- 고정 프레임(배경원/외곽링) + 회전 카드(6° 간격 60눈금 + N 문자 + 빨강/흰 삼각형)
- 부모의 `--rot` CSS 변수로 부드럽게 회전, StationOverlay RAF에서 매 프레임 갱신(360° 언랩)
- 효과: 영상만으로 드론이 향하는 방향을 한눈에 파악
---
## 6. 하단 측점바 & 타임라인 (`StationBar.tsx`, `Timeline.tsx`)
### 6.1 측점 정확도 개선
- **이동거리축 추가**: 드론 실제 GPS 이동량을 시간축에 반영 → 호버(공중 대기) 구간 가시화, 전진/후진 색 리본(주황/하늘색) 구분
- **영상 FPS 자동 적응**: 고정 29.97 대신 `마지막 프레임 / 재생시간`으로 영상별 계산 → 측점 배지 정확도 향상
- **kmExists 플래그**: 실제 측점값 존재 여부 구분 → 좌표 반경만으로 잡힌 통과는 라벨 제외
### 6.2 시설등급 필터 & 구조물 표시
- 1종/2종/3종/기타 선택적 표시(settingsStore 연동, 체크박스 즉시 반영)
- 긴 이름 2줄 분할(CJK 1.0/영문 0.55 가중치)
- 상/하행 평행 변형은 영상 진행방향에 따라 하나만 표시
- 동명 구조물 반복 통과 → 중앙 라벨 1개 + 통과점 점선 드롭 + 수평 브래킷
### 6.3 종점역 미도착 시각화
- 영상이 종점역에 미도달 시 트랙 우측을 회색(재생불가)으로 표시(상한 15%)
- 종점역 마커를 '속 빈 링' 스타일(미도착)로
---
## 7. 영상 플레이어 (`VideoPlayer.tsx`, `useVideoPlayer.ts`, `playerStore.ts`)
- **영상제어 UI 수평 통합**: 좌하단 컨트롤을 한 줄 가로 배열(`영상제어 | 배속 | 프레임 | 좌측패널 | 선형 | 드론궤적 | GPS/고도 | 시설등급`), flex-wrap 자동 줄바꿈
- **smoothTimeRef**: 벽시계 기준 단조 보간 시간 → StationBar가 ref로 직접 읽어 React 리렌더 없이 transform 갱신 → 60fps 부드러운 커서, 시크 시 즉시 재동기화
- **videoReady 게이트**: `loadeddata` 이후에만 오버레이 렌더 → 영상보다 먼저 그려지는 깜빡임 제거
- **영상 해상도 추적**: `videoWidth/videoHeight`로 object-fit:cover 정렬 정확화
- **폴더 드롭 지원**: `webkitGetAsEntry()` 디렉토리 재귀 순회 → 영상+측점/POI 폴더 통째 로드
- **드론 정보 HUD**: 현재 프레임 최근접 드론 GPS/고도 항상 표시
- **버튼 정렬**: `min-w-[96px]` 고정폭 + amber 활성색, 프레임번호 6자리 고정폭
---
## 8. 설정 중앙 관리 (`settingsStore.ts`, 신규)
Zustand + persist(`localStorage: 'ghivideo.settings'`)로 다음 표시 설정을 영속화한다.
| 키 | 기본값 | 용도 |
|----|--------|------|
| `gradeFilter` | `{1종:T, 2종:T, 3종:F, 기타:T}` | 시설종별 표시 필터 |
| `poiOverlapExclude` | `true` | 겹친 POI 숨김 |
| `showRoutePanel` | `true` | 좌측 노선 패널 |
| `showStationDiag` | `false` | 측점 진단 HUD |
| `showCenterline` | `true` | 선형(중심선) 오버레이 |
| `showDronePath` | `true` | 드론 궤적 오버레이 |
- 누락된 키는 기본값으로 자동 보충(스키마 진화 대비)
- `isGradeVisible()` 공유 함수로 StationBar·RoutePanel·StationOverlay가 동일 규칙 적용
---
## 9. 서버: 고도 API 프록시 (`elevation.ts`, 신규)
- 브라우저 COEP/CSP 제약으로 클라이언트가 외부 DEM API를 직접 호출 불가 → **서버 중계**
- `GET /api/elevation?lat=36.4,36.5&lon=127.4,127.5` (배치, 콤마 구분)
- 응답: `{ elevation: number[], source }`
- 폴백 전략: ① opentopodata SRTM 30m(정밀) → ② open-meteo 90m(폴백)
- `app.ts``app.use('/api/elevation', elevationRouter)` 등록 (서버 변경은 이것 + 라우트 파일이 전부)
---
## 10. 타입/스토어 변경 요약
### `geo.ts` 신규/확장
- `GeoPoint.props`: CSV 원본 속성 `{k,v}[]` (팝업 표시)
- `RouteStructure`: `lengthM`(연장) · `category`(교량/터널/구교/역사) · `grade`(시설종별) · `props` 추가
- `DirectionChange`(신규): `station` · `from`/`to` · `atSeconds`
- `PoiOverride` / `PoiOverrideMap`(신규): 드래그 보정값
- `RouteMeta.endStationGapMeters`: 종점역 미도착 거리
- `FolderGeoData`: `structures` · `directionChanges` · `poiOverrides` 추가
### `geoStore.ts`
- POI 보정 분리관리: `basePois`(원본) + `poiOverrides`(보정맵) → `applyPoiOverrides()` 결과가 `pois`
- 신규 상태: `structures`, `directionChanges`
- 신규 메서드: `setPoiOverride()`, `clearPoiOverride()`, `setPoiOverrides()`
- `loadFromFolder()`: Promise.all 병렬 로딩으로 8개 파서 동시 실행
---
## 11. 사용자 체감 개선 총괄
| 항목 | 개선 효과 |
|------|----------|
| **라벨 안정성** | 떨림/튐 제거 — 이상치 거부 + 속도적응 평활 |
| **화면 가독성** | 겹친 POI/구조물 자동 숨김 |
| **정보 탐색** | 다중 팝업 + 컴팩트/전체 토글 + ESC 정리 |
| **위치 정확도** | 지오이드 보정 + DEM 실제 표고 + 역투영 편집 |
| **편집 편의** | 드래그 + 표고 슬라이더 + 세로화각 자동보정 + JSON 저장 |
| **측점 정확도** | 이동거리축(호버 가시화) + 영상별 FPS 자동 |
| **커서 부드러움** | smoothTimeRef 단조보간 → 60fps |
| **작업 효율** | 폴더 통째 드롭 로드, 설정 localStorage 영속 |
| **방향 인지** | 나침반 미니맵으로 드론 방위각 실시간 표시 |
| **오버레이 안정** | videoReady 게이트로 깜빡임 제거 |
---
*본 문서는 `DefVideo`와 `GhiVideo`의 소스 트리 전체 비교(`diff -r`) 및 주요 변경 파일의 라인 단위 분석을 통해 작성되었다.*