fix: 히스토리 검증 훅 UTF-8 강제 + 측점 맵핑 문서 추가

- guard-history-fields.py: Windows cp949 stdin에서 한글 필드 정규식
  미스매치로 발생하던 false block 및 에러 mojibake 수정 (stdin/stderr UTF-8 강제)
- docs/geo-station-mapping.html: 지리정보·측점·프레임 맵핑 구조 +
  station 기준 재생 설계 문서
- docs/history: 2026-06-17 맵핑 구조 분석 기록

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
minsung
2026-06-17 09:31:34 +09:00
parent c1b6f99032
commit eaf7134309
3 changed files with 350 additions and 0 deletions

View File

@@ -0,0 +1,69 @@
# 지리정보 + 측점 + 프레임 맵핑 구조 분석
**작업일**: 2026-06-17
**작업명**: 지리정보·측점·프레임 맵핑 구조 소스 분석 + station 기준 재생 HTML 문서화
**소요 시간**: 18분
**Context 사용량**: input 60k / output 8k tokens
**이슈**: #0
---
## 목적
영상 프레임 ↔ 측점 ↔ 지리정보(POI/중심선)가 현재 소스에서 어떻게 맵핑되는지 코드 기반으로 파악. 추가로 "시간 기준이 아닌 station(측점) 기준 재생"을 위한 맵핑 구조를 HTML 문서로 출력.
## 분석 대상 파일
- [server/src/services/geoMatch.ts](../../server/src/services/geoMatch.ts) — 서버 투영/매칭 (평면근사 ENU)
- [server/src/routes/geo.ts](../../server/src/routes/geo.ts) — geo API 엔드포인트
- [client/src/utils/geoProjection.ts](../../client/src/utils/geoProjection.ts) — 클라 투영 (EPSG:5186 TM, Python advanced_tuner_v2.py 동일)
- [client/src/components/overlay/StationOverlay.tsx](../../client/src/components/overlay/StationOverlay.tsx) — 실시간 캔버스 오버레이
- [client/src/components/geo/StationVerify.tsx](../../client/src/components/geo/StationVerify.tsx) — 측점→프레임 점프 검증 패널
- [client/src/components/player/VideoPlayer.tsx](../../client/src/components/player/VideoPlayer.tsx) — frame↔time 변환
## 핵심 결론
### 맵핑 키 = 프레임 번호
- 드론 CSV의 `frame_cnt` = 영상 프레임 ↔ GPS+자세 연결고리
- `time = frame / fps`
- **fps 불일치 발견**: 클라 `VIDEO_FPS = 30000/1001 = 29.97`, 서버 `DEFAULT_FPS = 30`. seek는 frame번호 기반이라 실害 적으나 서버 `FrameMatch.time` 부정확.
### 데이터 소스 (GEO_DATA_DIR 기본 samplevideo/)
1. `*회덕*.csv` — 드론 비행로그 (frame_cnt, lat, lon, alt, yaw, pitch, roll, focal_len)
2. `building/*POI*위경도*.csv` — 지장물 (타원체고 버전 우선)
3. `building/*측점*위경도*.csv` — 측점
4. `pythonsource/input/center.csv` — 선로 중심선 224점 (타원체고 col5)
5. `*.srt` — terrain offset (로드만, 거의 미사용)
### 투영 파이프라인
```
geo(lat,lon,z) → ENU world(m) → 카메라 상대벡터 → 회전(yaw/pitch/roll) → 핀홀 투영 px,py(0~1)
```
- 월드 원점 = **첫 측점** (측량 기준점), 드론 frame[0] 아님 → 드론 GPS 오차 회피
- 서버/클라 투영 **구현 2벌** (서버=cos(lat) 평면근사, 클라=EPSG:5186 TM proj4) — 정밀도 차이
### 두 방향 질의
- `findFramesForPoi(name)`: 측점명 → 전 프레임 투영 → FOV 안 → 연속구간 그룹화(GAP=30) → 구간별 중심 프레임. (StationVerify 점프)
- `findPoisForFrame(n)`: 프레임 → 보이는 POI 목록.
### 클라 실시간 오버레이
- idle time에 `Map<frameNum, {stationLabels, poiMarkers}>` 사전계산
- RAF 루프: Map 조회 + frameNum→+1 보간 + EMA 스무딩 → canvas
- 측점은 투영 전 가장 가까운 중심선 점에 스냅 (nearestCL, z도 중심선값)
- smoothFrame: 드론 자세 ±N프레임 이동평균으로 GPS/IMU 노이즈 제거
### 측점 순서
- `stationOrder`: title `\d+K\d+` 파싱 = km측점 ("12K345" = 12.345km). 서버/클라 동일 함수.
## Station 기준 재생 관점
현재 구조는 "시간(프레임) → 지리정보" 단방향 표시 중심. station 기준 재생 = **측점 → 대표 프레임 → seek** 역방향이 핵심.
- 이미 존재하는 빌딩블록: `findFramesForPoi` (측점→최적 프레임), `onSeekToFrame` (프레임→`currentTime(frame/fps)`).
- station 기준 타임라인을 만들려면: 전 측점에 대해 `findFramesForPoi` 1회 일괄 호출 → 측점별 대표 프레임 테이블 구성 → km 순 정렬 → 측점 간 이동 = seek.
- 산출물: 맵핑 구조 + station 기준 재생 설계를 정리한 HTML (`docs/geo-station-mapping.html`).
## 후속 참고
- fps 불일치(30 vs 29.97) 정리 검토 가치 있음
- 서버/클라 투영 알고리즘 통일 여부 결정 필요 (현재 클라가 정밀)
- station 기준 재생용 측점→프레임 테이블 API(`/api/geo/station-index` 등) 신설 검토