제주 드론 도로영상 지원: DJI 로그 어댑터·분할영상 연속재생·카메라 자동감지·오버레이 개선
- 제주 DJI 시간기반 비행로그(CSV) 파싱 어댑터: 세그먼트 창 정렬, 가상 fps 환산 - 분할 영상(이름 오름차순) 연속재생 + 좌상단 영상목록 콤보(선택 재생), 세그먼트별 드론 로그 재정렬 - KML 다중 파일 병합 파싱: STA 체이니지 측점(71개) + 지장물 신포맷(타입/이름/텍스트박스_색상) - 시설물 라벨: 지정색 배경 박스 + 흰 글자, 팝업 중앙 정렬, 호버 구간 깜빡임 수정 - 카메라 자동감지(djmd: ZenmuseP1, focal 29.9mm) + <영상명>.camera.json PC 저장/폴더 자동 적용 - Yaw 자동추정(GPS 진행방위 기반) + 라벨 드래그 Yaw 역산 모드 - 스테이션바: GPS 이동거리 진행도(호버 시 정지), 노선밖 거리 표기, 원거리 구조물 마크 제외 - 서버: /api/camera 라우트, ecosystem 포트 54000·제주 데이터 경로 - docs/history: 작업 이력 21건 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
# 2026-07-09 54000 포트 외부 접근 셋팅
|
||||
|
||||
**소요 시간**: 15분
|
||||
**Context 사용량**: input 95k / output 10k tokens
|
||||
|
||||
## 작업 내용
|
||||
|
||||
GhiVideo_v4를 54000 포트로 서비스하고 다른 컴퓨터에서 접근 가능하도록 셋팅.
|
||||
|
||||
### 완료 항목
|
||||
|
||||
1. **의존성 설치 + 빌드**
|
||||
- 시스템 기본 node(v12)로 `npm install` 실패 (better-sqlite3 gyp 오류) → nvm Node 20 (v20.20.2)으로 재설치 성공
|
||||
- `npm run build` (shared → server → client) 완료
|
||||
|
||||
2. **PM2 앱 이름 충돌 해결**
|
||||
- 원본 GhiVideo와 v4의 ecosystem.config.js가 둘 다 `name: "ghiVideo"` → `pm2 start` 시 원본 앱이 v4 env(PORT=54000)로 재시작되는 사고 발생
|
||||
- v4의 ecosystem.config.js 이름을 `ghiVideo-v4`로 변경
|
||||
- 꼬인 엔트리 삭제 후 양쪽 재기동: 원본 `ghiVideo`(55000) 복구 + `ghiVideo-v4`(54000) 신규 기동
|
||||
- 둘 다 `/api/videos` 200 응답 확인, `pm2 save` 완료
|
||||
|
||||
3. **Windows 포트포워딩 (미완 — 사용자 실행 필요)**
|
||||
- WSL2 NAT 모드 (WSL IP: 172.21.50.250). 기존 포트들(55000, 5173, 8080 등)은 netsh portproxy + 방화벽 규칙으로 개방된 패턴 확인
|
||||
- 54000용 스크립트를 `C:\Users\Public\ghivideo-v4-54000-setup.ps1`에 작성
|
||||
- 관리자 권한 자동 실행은 권한 정책상 차단 → **사용자가 관리자 PowerShell에서 직접 실행 필요**
|
||||
|
||||
### 재부팅 내구성 검토 (같은 세션 후속 작업)
|
||||
|
||||
- Windows 루프백 → WSL 포워딩 확인: `127.0.0.1:54000` 200 응답 → portproxy `connectaddress=127.0.0.1` 방식이면 **WSL IP가 바뀌어도 규칙이 안 깨짐**. 설정 스크립트를 이 방식으로 갱신함
|
||||
- 기존 규칙들(55000, 5173 등)은 WSL IP(172.21.50.250) 하드코딩 — 재부팅으로 IP 바뀌면 깨질 수 있음. 동일하게 127.0.0.1로 바꾸는 것 권장
|
||||
- **pm2 자동 시작 없음**: pm2 systemd 유닛 없음, 시작프로그램의 AI-Video-Brief.vbs는 ai_video_brief 전용. 재부팅 후 `pm2 resurrect` 수동 실행 필요. 시작프로그램 vbs 자동 생성은 보안 정책 차단 → 사용자 직접 생성 필요
|
||||
|
||||
### 다음 단계 참고
|
||||
|
||||
- 관리자 PowerShell에서 1회 실행: `powershell -ExecutionPolicy Bypass -File C:\Users\Public\ghivideo-v4-54000-setup.ps1` (portproxy 127.0.0.1 방식 + 방화벽 규칙)
|
||||
- v4 서버 코드 기본 포트는 3030(`server/src/config.ts`)이나, PM2 env로 54000 주입됨
|
||||
@@ -0,0 +1,37 @@
|
||||
# 2026-07-09 제주 중산간 도로 데이터 플레이어 연결
|
||||
|
||||
**소요 시간**: 10분
|
||||
**Context 사용량**: input 130k / output 6k tokens
|
||||
|
||||
## 작업 내용
|
||||
|
||||
`D:\_project\...\제주 중산간 도로 노선 동영상 비행 로그\경로 1-1` 데이터를 GhiVideo_v4 플레이어에 연결.
|
||||
|
||||
### 데이터 구성
|
||||
|
||||
- DJI 드론 영상 4개 (DJI_20260629121835_0002~0005.MP4, 총 12GB)
|
||||
- H.264, 4K(3840x2160), 59.94fps, 개당 약 197초(0005는 더 짧음)
|
||||
- 비행로그 CSV: `제주 중산간도로 동영상 경로 1-1.csv` — **시간 기반**(100ms 간격), 컬럼: `time(millisecond), latitude, longitude, X(E), Y(N), altitude_above_seaLevel, compass_heading, pitch, roll, gimbal_heading, gimbal_pitch, gimbal_roll`
|
||||
- `제주_중산간도로.kml` — 도로 노선
|
||||
|
||||
### 완료 항목
|
||||
|
||||
- ecosystem.config.js의 `VIDEOS_DIR`/`GEO_DATA_DIR`를 `/mnt/d/...` 제주 경로로 변경, pm2 재기동 + save
|
||||
- `/api/videos` 4개 영상 인식, `/api/meta` 정상 (H.264 확인 → HLS 변환 시 `-c copy` 가능)
|
||||
- Range 스트리밍 검증: 앞부분 55MB/s, 2GB 지점 seek 34MB/s (4K60 비트레이트 약 19MB/s 대비 충분)
|
||||
|
||||
### 미완료 / 알려진 제약 (geo 비행로그 연동)
|
||||
|
||||
현재 geoMatch 서비스는 회덕 데이터셋 전용이라 제주 CSV를 읽지 못함:
|
||||
|
||||
1. 드론 CSV 파일명 `'회덕'` 하드코딩 필터 (`geoMatch.ts` loadFrames)
|
||||
2. 컬럼 불일치: 기대 `frame_cnt/latitude/longitude/altitude/yaw/pitch/roll/focal_len` vs 제주 `time(millisecond)/compass_heading/gimbal_heading/...` → 시간→프레임 변환 어댑터 필요
|
||||
3. POI/측점(`building/` 폴더) 데이터가 제주 데이터셋에 없음 → POI 검색 기능은 데이터 부재로 불가
|
||||
4. 영상이 4개로 분할 → 로그 시간을 (영상 파일, 영상 내 시점)으로 매핑하는 오프셋 계산 필요
|
||||
5. KML은 중심선(center.csv 대체) 소스로 활용 가능
|
||||
|
||||
### 다음 단계 참고
|
||||
|
||||
- 업로드 기능(tus) 사용 시 D: 폴더(VIDEOS_DIR)에 저장됨 — 의도 확인 필요
|
||||
- HLS/프레임/썸네일 산출물은 기존대로 프로젝트 `storage/`에 생성됨
|
||||
- 제주 geo 어댑터 작업 시 요구 기능(경로 시각화, 프레임별 위치 표시 등) 범위 확정 필요
|
||||
@@ -0,0 +1,49 @@
|
||||
# 2026-07-09 제주 DJI 비행로그 드론궤적 어댑터
|
||||
|
||||
**소요 시간**: 30분
|
||||
**Context 사용량**: input 175k / output 12k tokens
|
||||
|
||||
## 문제
|
||||
|
||||
제주 데이터 폴더 선택 시 드론 궤적 오버레이가 표시되지 않음.
|
||||
|
||||
## 원인
|
||||
|
||||
클라이언트 지리정보 파이프라인(폴더 선택 → `geoData.ts` 파싱 → geoStore → StationOverlay 투영)이 회덕 v2.0 프레임 기반 CSV(`frame_cnt,latitude,…,yaw,pitch,roll,focal_len`) 전용이었음. 제주 CSV는 DJI 비행로그 시간 기반(`time(millisecond),latitude,…,compass_heading,gimbal_heading,…`)이라:
|
||||
|
||||
1. 위치 폴백 인덱스로 컬럼이 밀림 — frame←ms값(89500~), altitude←X(E) 평면좌표, yaw←Y(N), focalLen←pitch 등 전부 오염
|
||||
2. `effectiveFps`(=마지막 frame ÷ 영상길이) 자기보정이 깨져 시간↔프레임 변환 실패
|
||||
3. 분할 영상 4개(_0002~_0005) 중 첫 영상 길이(197.1s)에 로그 전체(672.6s)가 압축 매핑됨
|
||||
4. 경로표고 슬라이더 상한(중심선 z 기반, 폴백 60m)이 제주 지형(드론 148m ASL)에 못 미침
|
||||
|
||||
## 수정 내용
|
||||
|
||||
### client/src/utils/geoData.ts
|
||||
|
||||
- `parseDroneFrames`: `time(millisecond)` 헤더 감지 시 DJI 시간 기반 브랜치로 분기
|
||||
- 컬럼: lat/lon/`altitude_above_seaLevel(meter)`, 자세는 gimbal_* 우선(기체 compass/pitch/roll 폴백), focalLen=24mm 기본
|
||||
- `frame = ((time−t0)/1000 − offsetSec) × 59.94(가상fps)` — effectiveFps 자기보정과 정합
|
||||
- `SegmentWindow`(offsetSec/durationSec)로 재생 영상 구간 밖 행 클리핑
|
||||
- `findVideoFiles`: 영상 이름순 정렬(DJI 분할본 _0002 먼저 확정)
|
||||
- `probeVideoDuration`: objectURL + `preload=metadata`로 영상 길이만 측정
|
||||
- `loadFolderGeoData`: 첫 영상 길이 측정 → 세그먼트 창 전달, 분할본 감지 시 콘솔 경고
|
||||
- `parseRouteMeta`: 임의 접두 `*.route.json` 인식 (제주 route.json 파일명 대응)
|
||||
- `parseKmz`: 체이니지 표점(`0+100` 형식) Point는 POI 제외 (z=0이라 투영 깨짐 방지)
|
||||
- `kmzMissing`: KML/KMZ 파일 자체가 없을 때만 alert (파일은 있으나 POI 0건은 콘솔 경고만)
|
||||
|
||||
### client/src/components/overlay/StationOverlay.tsx
|
||||
|
||||
- `pathZMax`: 중심선 z에 더해 드론 고도(ASL) 최대값도 상한 근거에 포함 → 제주에서 슬라이더 159m까지
|
||||
|
||||
## 검증
|
||||
|
||||
- `npm run build -w client` (tsc + vite) 통과, 54000 포트가 새 번들(index-Cp4zEgX_.js) 서빙 확인
|
||||
- 실제 CSV로 파싱 수학 시뮬레이션: 6649행 → 세그먼트 내 1947행, frame 0~11814, effectiveFps 59.9248→59.94 스냅, 영상 t=100s ↔ 로그 t=100.00s 왕복 정합, 자세값 정상(yaw 65.1°, pitch −15°, alt 147.9m)
|
||||
|
||||
## 남은 제약 / 다음 단계 참고
|
||||
|
||||
- **분할 영상 2~4번째(_0003~_0005) 미지원** — 세그먼트 선택 UI + offsetSec(누적 길이) 연결 필요. 로그 폭(672.6s) ≈ 영상 총길이(671.7s) 검증됨, 오프셋: 0 / 197.147 / 394.211 / 591.475s
|
||||
- 드론 고도 거의 일정(147.8~148.6m ASL) — 도로 표고는 그 아래. 경로표고 슬라이더로 90~140m 부근 튜닝 필요
|
||||
- KML LineString(도로 중심선)은 고도가 전부 0이라 중심선(선형 오버레이) 소스로 미사용
|
||||
- 측점/POI 데이터가 제주 데이터셋에 없음 — KML 체이니지 표점(0+000~) 활용하려면 표고 부여 필요
|
||||
- route.json의 durationSec(568)이 실제 영상 총길이(671.7s)와 불일치 — 사용자 확인 필요
|
||||
@@ -0,0 +1,48 @@
|
||||
# 2026-07-09 분할 영상 연속재생 (이름 오름차순 자동 전환)
|
||||
|
||||
**소요 시간**: 25분
|
||||
**Context 사용량**: input 210k / output 8k tokens
|
||||
|
||||
## 작업 내용
|
||||
|
||||
폴더 내 분할 영상(DJI _0002~_0005)이 끝나면 이름 오름차순으로 다음 영상을 자동 재생.
|
||||
전환 시 드론 로그(시간 기반)를 해당 세그먼트의 누적 오프셋으로 재정렬해 궤적 정합 유지.
|
||||
|
||||
## 수정 파일
|
||||
|
||||
### client/src/types/geo.ts
|
||||
- `FolderGeoData`에 `videoFiles`(이름 오름차순), `videoDurations`, `folderFiles` 추가
|
||||
|
||||
### client/src/utils/geoData.ts
|
||||
- `loadFolderGeoData`: 폴더 내 **모든** 영상 길이를 메타데이터로 측정(기존엔 첫 영상만),
|
||||
분할본 감지 시 재생 순서 콘솔 로그. 측정값·원본 파일 목록을 반환값에 포함
|
||||
|
||||
### client/src/store/geoStore.ts
|
||||
- 상태 추가: `videoFiles` / `videoDurations` / `videoIndex` / `folderFiles`
|
||||
- **`loadSegment(index)` 액션 신설**: 누적 오프셋(앞 세그먼트 길이 합) 계산 →
|
||||
`parseDroneFrames`를 세그먼트 창으로 재실행해 frames 교체, videoIndex 갱신, 재생할 File 반환
|
||||
- 길이 측정 실패 세그먼트가 있으면 궤적만 비우고(frames=[]) 재생은 계속
|
||||
- `baseName`은 폴더(첫 영상) 기준 유지 — 카메라 보정 localStorage 연속성
|
||||
- `origin`도 폴더 로드 시점 값 유지 — ENU 기준점 일관성(전환 시 투영 점프 방지)
|
||||
|
||||
### client/src/components/player/VideoPlayer.tsx
|
||||
- Video.js `ended` 이벤트 핸들러 추가: 폴더 기반 로컬 재생 + 분할본 2개 이상일 때만
|
||||
`loadSegment(next)` → `loadLocalFile(file)` → `player.play()` (ended 직후는 paused라 직접 재생)
|
||||
- 서버 스트림/단일 파일 재생은 대상 아님 (source.kind !== 'local' 가드)
|
||||
|
||||
## 검증
|
||||
|
||||
- `npm run build -w client` (tsc + vite) 통과, 54000 포트 새 번들(index-BA4g81-1.js) 서빙 확인
|
||||
- 실제 CSV로 4개 세그먼트 오프셋 시뮬레이션:
|
||||
- 세그먼트 1: offset 0s, 1947행, effFps 59.925 → 59.94 스냅 ✓
|
||||
- 세그먼트 2: offset 197.1s, 1963행, effFps 59.935 ✓
|
||||
- 세그먼트 3: offset 394.2s, 1936행, effFps 59.920 ✓
|
||||
- 세그먼트 4: offset 591.5s, 793행, effFps 59.877 ✓ (±10% 스냅 범위 내)
|
||||
- 행 합계 6639/6649 — 경계 인접 소수 행만 제외, 정상
|
||||
|
||||
## 다음 단계 참고
|
||||
|
||||
- 세그먼트 수동 선택 UI(목록에서 특정 세그먼트로 점프)는 미구현 — `loadSegment`가 이미 있어
|
||||
UI만 붙이면 됨 (`useGeoStore.getState().loadSegment(i)` + `loadLocalFile`)
|
||||
- 마지막 세그먼트(_0005) 종료 시 정지 (반복 재생 없음 — 요구사항 밖)
|
||||
- 전환 시 1~2초 로딩 공백 가능(objectURL 교체) — 프리로드 최적화는 필요 시
|
||||
@@ -0,0 +1,44 @@
|
||||
# 2026-07-09 스테이션바 진행도 — 드론 정지 시 커서 정지 (이동거리축 폴백)
|
||||
|
||||
**소요 시간**: 10분
|
||||
**Context 사용량**: input 240k / output 5k tokens
|
||||
|
||||
## 문제
|
||||
|
||||
드론이 이동을 멈췄는데(호버링) 하단 스테이션바 커서는 계속 증가.
|
||||
|
||||
## 원인
|
||||
|
||||
StationBar의 진행도 축(chainRef: 시간→누적 이동거리 비율)은 **측점 폴리라인 체이니지 기반**으로만
|
||||
구성됨 (`frames + stations + stationLine` 필요). 제주 데이터셋은 측점이 없어 `ready=false`
|
||||
→ `cumFracAtTime`이 **시간 선형 폴백**(`t / duration`) 사용 → 드론이 멈춰도 커서는 시간 비례로 전진.
|
||||
|
||||
실데이터 확인: 세그먼트 2에서 드론이 t=8~128s(120초), t=178~198s 두 번 호버링 —
|
||||
사용자가 관측한 구간과 일치.
|
||||
|
||||
## 수정 내용 — client/src/stationbar/StationBar.tsx
|
||||
|
||||
- precompute useEffect에 **측점 없음 폴백** 추가: 드론 프레임만 있으면 GPS(위경도)를
|
||||
±8프레임 이동평균으로 평활 후 프레임 간 수평거리 누적 → 정규화(frac)해 chainRef 구성.
|
||||
측점 기반 기능(배지/구간색/구조물 마크)은 기존대로 ready=false 비활성 유지
|
||||
- `cumFracAtTime` / `timeAtFrac` 가드에서 `!ready` 제거 — chainRef 존재 여부만 확인
|
||||
(폴백 chainRef도 사용). 바 클릭 seek도 이동거리축 역변환으로 동작
|
||||
- depChain/arrChain은 방향색 전용이라 폴백에선 더미(0/1)
|
||||
|
||||
## 검증 (실데이터 시뮬레이션, 세그먼트 2)
|
||||
|
||||
| 시점 | 기존(시간선형) | 수정(이동거리) |
|
||||
|------|--------------|--------------|
|
||||
| t=8s (호버 시작) | 4.1% | 8.0% |
|
||||
| t=68s (호버 중) | 34.5% | 8.4% |
|
||||
| t=128s (호버 끝) | 65.0% | 9.0% |
|
||||
|
||||
호버 120초 동안 커서가 사실상 정지(+1.0%p). 세그먼트 2 총 이동거리 460.2m.
|
||||
tsc+vite 빌드 통과, 54000 포트 새 번들(index-quVWnwfd.js) 서빙 확인.
|
||||
|
||||
## 다음 단계 참고
|
||||
|
||||
- 정지 구간에서 커서가 멈추므로, 그 동안 시간은 흐르는데 커서가 안 움직이는 게 정상 동작
|
||||
(좌측 시간 배지는 계속 증가 — 서로 다른 축)
|
||||
- 드론이 전 구간 정지(총 이동거리 0)인 극단 케이스는 frac 전부 0 → 커서 시작점 고정
|
||||
- 세그먼트 전환(loadSegment) 시 frames 교체 → chainRef 자동 재구성됨
|
||||
@@ -0,0 +1,53 @@
|
||||
# 2026-07-09 KML 시설물/측점 정보 임포트 (ExtendedData 지원)
|
||||
|
||||
**소요 시간**: 12분
|
||||
**Context 사용량**: input 265k / output 6k tokens
|
||||
|
||||
## 작업 내용
|
||||
|
||||
KML 파일에서 시설물 정보를 가져올 수 있도록 parseKmz 확장. 제주 KML의 STA(체이니지 측점)
|
||||
71개가 측점으로 로드되어 측점 파이프라인 전체(스테이션바·노선패널·검색·오버레이) 활성화.
|
||||
|
||||
## 배경 — 제주 KML 구조
|
||||
|
||||
```
|
||||
Folder 선형중심선: LineString (도로 중심선, CAD 내보내기)
|
||||
Folder STA: Point 71개 (0+000 ~ 6+998, 100m 간격)
|
||||
└ ExtendedData/SchemaData/SimpleData: STA(이름), lat, lon, 5186_x/y, km(float)
|
||||
```
|
||||
|
||||
기존 parseKmz 는 description CDATA HTML 표(회덕 지오코딩 산출물)만 속성으로 읽었고,
|
||||
체이니지 Point 는 직전 작업에서 POI 오염 방지를 위해 스킵 처리했었음.
|
||||
|
||||
## 수정 내용 — client/src/utils/geoData.ts
|
||||
|
||||
1. **SimpleData 속성 수집**: `ExtendedData/SchemaData/SimpleData` 를 description HTML 표와
|
||||
동일하게 props 로 통합 — CAD·GIS 내보내기 KML의 속성이 기존 폴더 분기(교량/터널/구교/출입문)에서도
|
||||
그대로 동작 (향후 시설물 폴더가 있는 KML 대응)
|
||||
2. **체이니지 측점 → station**: 이름 `N+NNN` 형식 또는 STA 속성 보유 Point 를
|
||||
`type='station'` 으로 수집. km 속성(단위 km) 우선, 없으면 이름 파싱.
|
||||
제목을 철도식 `NkNNN`("0k100")으로 변환 → stationOrder/stationKm 파이프라인 그대로 호환
|
||||
3. **parseKmz 반환에 `stations` 추가**, 로드 로그에 측점 수 표기
|
||||
4. **loadFolderGeoData**: CSV 측점(01)측점 — 실측 표고) 우선, 없으면 KML 측점 사용.
|
||||
KML 측점은 표고 미상(z=0) → **최근접 드론 프레임 고도 − 24m 근사**
|
||||
(도로 추종 저고도 비행 가정, 오버레이 기본 모드가 드론고도 기준이라 오차 영향 작음)
|
||||
|
||||
## 활성화되는 기능 (제주 데이터)
|
||||
|
||||
- 하단 스테이션바: 측점 체이니지 기반 이동거리축(ready=true), 최근접 측점 배지, 바 클릭 seek
|
||||
- 좌측 노선 패널(RoutePanel): 측점 71개 (0k000~6k998), 시점/종점명은 route.json(A/B)
|
||||
- 영상 오버레이: 측점 라벨 투영 (드론고도 − 이격 모드 기본)
|
||||
- GeoSearch: 측점명 검색/자동완성
|
||||
|
||||
## 검증
|
||||
|
||||
- tsc + vite 빌드 통과, 54000 포트 새 번들(index-BvbrGp3O.js) 서빙
|
||||
- KML STA 데이터 레벨 검증: 71개 전부 변환, 제목 0k000~6k998, stationKm 파싱 유효, 단조증가
|
||||
|
||||
## 다음 단계 참고
|
||||
|
||||
- KML 노선은 0~7km인데 경로 1-1 비행은 그중 약 1.9km 구간만 커버 — RoutePanel 마커가
|
||||
일부 구간에서만 움직이는 것이 정상
|
||||
- 교량/터널 등 실제 시설물 Placemark 가 있는 KML 이 오면 기존 폴더명 분기(교량/터널/구교/출입문)로
|
||||
자동 임포트됨 — SimpleData 속성(연장(m), 구분 등)도 이제 인식
|
||||
- 선형중심선 LineString 은 미사용 (측점 폴리라인으로 중심선 생성 — 기존 로직)
|
||||
@@ -0,0 +1,51 @@
|
||||
# 2026-07-09 지장물 KML 신포맷 파싱 (다중 KML + 라벨 색상)
|
||||
|
||||
**소요 시간**: 13분
|
||||
**Context 사용량**: input 300k / output 8k tokens
|
||||
|
||||
## 문제
|
||||
|
||||
새로 받은 `제주_중산간도로_지장물.kml`(구글어스 Pro에서 CSV 변환, 지장물 33개)의
|
||||
좌표/라벨 색상이 화면에 표시되지 않음.
|
||||
|
||||
## 원인
|
||||
|
||||
1. **parseKmz 가 KML 을 1개만 읽음** — `files.find(...)` 라 폴더에 KML 이 2개(측점 KML + 지장물 KML)면
|
||||
첫 파일만 파싱되고 지장물 KML 은 통째로 무시됨 (핵심 원인)
|
||||
2. 신포맷은 폴더(교량/터널 등) 구분이 없고 Placemark 직속 + Schema
|
||||
(`타입/타입상세/텍스트박스_색상/이름/연장/X/Y/높이`) — 기존 desc HTML 표 기반 분기와 불일치
|
||||
3. 라벨 색상 개념이 렌더러에 없음 — 캔버스 라벨이 고정색(#64c8ff) 단일
|
||||
|
||||
## 수정 내용
|
||||
|
||||
### client/src/utils/geoData.ts
|
||||
- **parseKmz 다중 파일 병합**: 폴더 내 모든 .kml/.kmz 를 순회 파싱해 pois/structures/stations 병합
|
||||
(파일 단위 실패는 경고 후 건너뜀)
|
||||
- **신포맷(지장물 스키마) 브랜치**: `타입` SimpleData 존재로 감지 —
|
||||
- title=`이름`(폴백 name), category=`타입`(교차로/도로/교량/강.하천/마을/산/지장물/지장물L)
|
||||
- 좌표는 SimpleData X/Y(저정밀)가 아닌 **Point coordinates(고정밀)** 사용
|
||||
- `높이`는 표고가 아닌 구조물 높이 → z=0 유지(팝업 props 로만 노출)
|
||||
- 타입 교량/터널은 구조물(스테이션바·노선패널 마크)로도 중복 등록 (연장→lengthM)
|
||||
- **라벨 색**: `텍스트박스_색상` 한국어명(검정/남색/빨강/연두/주황/초록/파랑 등 13색) → CSS 매핑,
|
||||
없으면 인라인 IconStyle 색(KML aabbggrr → #rrggbb) 폴백 → `GeoPoint.labelColor`
|
||||
|
||||
### client/src/types/geo.ts
|
||||
- `GeoPoint.labelColor?: string` 추가
|
||||
|
||||
### client/src/components/overlay/StationOverlay.tsx
|
||||
- labelColor 를 poiCand → poiMarkers(라벨 캐시) → 캔버스 렌더로 전달
|
||||
- 십자 마커·라벨 텍스트를 지정 색으로 렌더, 어두운 색(검정·남색)은 외곽선을 밝게 반전(가독성)
|
||||
- CATEGORY_EMOJI 확장: 교차로🚦 도로🛣️ 강.하천🌊 마을🏘️ 산⛰️ 지장물L🏢
|
||||
|
||||
## 검증
|
||||
|
||||
- tsc+vite 빌드 통과, 54000 포트 새 번들(index-CLmojEn3.js) 서빙
|
||||
- 지장물 KML 데이터 검증: 33개 전부 파싱, 색상 매핑 33/33, 교량 3건 구조물 중복 등록,
|
||||
좌표 불량 0 (Point coordinates 고정밀 사용)
|
||||
- 기존 측점 KML(STA 71개)과 병합 로드 확인 (다중 KML)
|
||||
|
||||
## 다음 단계 참고
|
||||
|
||||
- 신포맷 감지 키는 `타입` SimpleData — 회덕 desc표/STA 스키마와 충돌 없음
|
||||
- styleUrl(#pointStyleMap1) → StyleMap 참조 해석은 미구현 (인라인 Style 이 항상 존재해 불필요)
|
||||
- 색상명 미정의 시(예: 새로운 색 이름) 인라인 IconStyle 색 폴백 → 그것도 없으면 기본 하늘색
|
||||
@@ -0,0 +1,52 @@
|
||||
# 2026-07-09 오버레이 라벨 표고 앵커 + 측점 거리 제한
|
||||
|
||||
**소요 시간**: 10분
|
||||
**Context 사용량**: input 340k / output 6k tokens
|
||||
|
||||
## 문제 (스크린샷 제보)
|
||||
|
||||
1. 빨간 측점 라벨(0k000~6k998)이 수평선 부근에 전부 겹쳐서 깨진 글자처럼 보임
|
||||
2. POI 라벨이 실제 지물보다 높게 떠서, 카메라(드론)가 전진하면 라벨이 계속 뒤로 밀림(시차 불일치)
|
||||
|
||||
## 원인
|
||||
|
||||
1. **측점 라벨엔 거리 제한이 없음** — POI는 maxPoiRange(기본 1000m) 필터가 있지만 측점은 없어서
|
||||
7km 앞 측점까지 전부 FOV 안 수평선 근처에 투영·중첩됨
|
||||
2. **라벨 표고 근사가 틀림** — 직전 작업에서 측점 z를 "드론고도−24m ≈ 124m"로 근사했으나
|
||||
실제 지면은 ≈60m(경로표고 기본값 58이 도로에 붙는 것으로 확인) → 라벨이 지면보다
|
||||
약 60m 공중에 떠서 원근 시차가 안 맞아 전진 시 라벨이 뒤로 흐름.
|
||||
드론고도 모드(pz=드론고도−이격24)도 동일하게 124m 평면이라 같은 증상
|
||||
|
||||
## 수정 내용
|
||||
|
||||
### client/src/utils/geoData.ts
|
||||
- KML 측점의 z 근사(드론고도−24) 제거 → z=0(표고 미상 표식) 유지
|
||||
|
||||
### client/src/components/overlay/StationOverlay.tsx
|
||||
- **표고 미상(z≤0) 라벨을 '경로 표고'(dronePathZ) 평면에 앵커**:
|
||||
- 측점 라벨 sz: 유효 z 없으면 PATH_Z(경로표고) 사용
|
||||
- POI gz 폴백 체인: 보정값 → 중심선 z(>0만) → POI z(>0만) → PATH_Z
|
||||
- 경로표고 슬라이더 변경 시 라벨 캐시 재계산(500ms 디바운스, 기존 트리거에 dronePathZ 추가)
|
||||
- → **궤적을 도로에 맞추는 슬라이더 하나로 측점/POI 라벨 높이가 함께 보정됨**
|
||||
- **측점 라벨에도 maxPoiRange 거리 제한 적용** — 원거리 측점 수평선 뭉침 해소
|
||||
- **poiDroneHeight 기본값 true→false(지면고도 기준)**:
|
||||
- 실측 z 데이터셋(회덕)은 지물에 정확히 붙음(원래 이 모드가 정확)
|
||||
- 표고 미상 데이터셋(제주)은 경로표고 앵커로 일괄 보정 가능
|
||||
- 드론고도 모드는 UI 토글로 여전히 선택 가능
|
||||
|
||||
## 검증
|
||||
|
||||
- tsc+vite 빌드 통과, 54000 포트 새 번들(index-Nv1HLuS3.js) 서빙
|
||||
- 기하 검증(계산): 지면 60m/드론 148m 기준, 124m 라벨은 300m 전방 지물 대비 화면상 약
|
||||
12% 위쪽에 투영(수평선 방향) → 전진 시 벌어짐. PATH_Z(58) 앵커 시 지물과 동일 평면 → 시차 일치
|
||||
|
||||
## 사용법 (사용자 안내)
|
||||
|
||||
- 새로고침 후 폴더 재선택 → '경로 표고' 슬라이더로 파란 궤적을 도로 노면에 맞추면
|
||||
측점·POI 라벨도 같은 평면으로 따라옴 (기본 58m가 대략 맞음)
|
||||
- 측점 라벨 표시 거리는 'POI 표시범위'(maxPoiRange) 슬라이더로 조절
|
||||
|
||||
## 다음 단계 참고
|
||||
|
||||
- 지형 기복이 큰 구간은 단일 평면 앵커의 한계 있음 — 필요 시 구간별 z 또는 DEM 도입 검토
|
||||
- RoutePanel 가시범위(초록 박스) 계산은 여전히 pt.z(0) 사용 — 부정확할 수 있으나 부차적
|
||||
@@ -0,0 +1,31 @@
|
||||
# 2026-07-09 영상 재생목록 패널 (분할 영상 선택 재생)
|
||||
|
||||
**소요 시간**: 10분
|
||||
**Context 사용량**: input 380k / output 5k tokens
|
||||
|
||||
## 작업 내용
|
||||
|
||||
폴더 로드 시 폴더 내 영상 파일 목록(이름 오름차순)을 패널로 표시하고,
|
||||
목록에서 원하는 영상을 클릭해 바로 선택 재생할 수 있게 함.
|
||||
|
||||
## 수정 내용 — client/src/components/player/VideoPlayer.tsx
|
||||
|
||||
- **재생목록 패널** (우하단, 재생바 위):
|
||||
- 폴더 내 영상 파일명 + 길이(m:ss), 현재 재생 항목 ▶ + 주황 하이라이트
|
||||
- 항목 클릭 → 해당 세그먼트로 즉시 전환 재생 (geoStore.loadSegment 로 드론 로그도
|
||||
해당 구간 누적 오프셋으로 재정렬 — ended 자동 전환과 동일 경로)
|
||||
- 현재 재생 중인 항목 재클릭 → 처음(0초)부터 재생
|
||||
- 목록이 길면 스크롤 (max-h-48)
|
||||
- 헤더에 "N/M" 진행 표시. ended 자동 전환 시에도 videoIndex 구독으로 하이라이트 자동 갱신
|
||||
- **'영상목록 N/M' 토글 버튼**: 하단 컨트롤 행(드론궤적 버튼 옆). 기본 표시(ON)
|
||||
- `fmtDur` 헬퍼(초→"m:ss"), geoStore videoFiles/videoDurations/videoIndex 구독 추가
|
||||
- 로컬(폴더) 재생일 때만 표시 (source.kind === 'local')
|
||||
|
||||
## 검증
|
||||
|
||||
- tsc+vite 빌드 통과, 54000 포트 새 번들(index-DLUIvK3Q.js) 서빙
|
||||
|
||||
## 다음 단계 참고
|
||||
|
||||
- 세그먼트 전환 시 약 1초 로딩 공백(objectURL 교체) — 필요 시 다음 세그먼트 프리로드 검토
|
||||
- 패널 위치는 우하단 고정 — 캡처 목록 등 다른 우측 UI 와 겹치면 위치 조정 필요
|
||||
@@ -0,0 +1,50 @@
|
||||
# 2026-07-09 드론 카메라 자동 감지 + 라벨 박스 스타일 + 영상목록 콤보
|
||||
|
||||
**소요 시간**: 16분
|
||||
**Context 사용량**: input 430k / output 12k tokens
|
||||
|
||||
## 작업 내용 (요청 4건)
|
||||
|
||||
### 1. 드론 카메라 정보 자동 감지 → 카메라 파라미터 적용 (POI 매핑 어긋남의 핵심 원인)
|
||||
|
||||
DJI MP4의 `djmd` 데이터 트랙(프레임별 protobuf 텔레메트리)을 분석해 필드 구조 해독:
|
||||
|
||||
| 필드 | 내용 | 제주 값 |
|
||||
|------|------|--------|
|
||||
| 1.1.2 | 카메라 모델 | **DJI ZenmuseP1** (풀프레임 측량 카메라) |
|
||||
| 1.7 / 1.6 / 1.8 | 해상도 / fps | 3840×2160 / 59.94 |
|
||||
| 6.4 / 6.5 / 6.6 | 위경도(라디안) / 타원체고(mm) | 33.40°/126.29°/174.4m |
|
||||
| **8.6** | **초점거리 (0.1mm, 35mm 환산)** | **299 → 29.9mm** |
|
||||
|
||||
- 기존 기본값 24mm vs 실제 29.9mm(DL 24mm 렌즈 × 4K 비디오 크롭 ≈1.25) → FOV 과대 → POI 어긋남
|
||||
- `client/src/utils/cameraMeta.ts` 신규: 브라우저에서 영상 File 앞 8MB를 읽어 djmd 첫 샘플 파싱
|
||||
(모델 문자열 탐색 → 샘플 경계 역추적 → protobuf 필드 수집). 실파일 검증 완료
|
||||
- `loadFolderGeoData`: 감지 정보를 FolderGeoData/geoStore(cameraInfo)에 적재, 드론 프레임 focalLen에도 반영
|
||||
- StationOverlay: **저장된 보정(localStorage calib)이 없는 데이터셋은 감지 focal 자동 적용**,
|
||||
카메라 파라미터 패널에 "📷 모델 · 초점 · fps" 표시 + **[적용] 버튼**(수동 적용 — 이미 튜닝한
|
||||
데이터셋은 자동 적용을 건너뛰므로 이 버튼 사용)
|
||||
|
||||
### 2. '드론 높이와 POI 이격거리' 슬라이더 최대값 120 → 300
|
||||
|
||||
### 3. 영상목록 → 좌상단 타이틀 아래 콤보 드롭다운
|
||||
|
||||
- 우하단 패널/하단 버튼 제거 → 노선 배너(타이틀) 아래 `left:8, top:paramTop` 콤보 버튼
|
||||
(현재 파일명 + n/m + ▼). 클릭 시 목록이 아래로 펼쳐지고 선택 시 즉시 전환·접힘
|
||||
- RoutePanel이 paramTop+40부터 시작해 겹침 없음
|
||||
|
||||
### 4. POI 라벨: 십자/아이콘 제거, 지정색 배경 + 흰 글자
|
||||
|
||||
- 캔버스 렌더를 "텍스트 박스"로 변경: 배경 = KML 텍스트박스_색상(라운드 사각형), 글자 = 흰색
|
||||
- POI 지점에 가로 중앙 정렬, 히트박스도 박스 영역으로 갱신
|
||||
|
||||
## 검증
|
||||
|
||||
- tsc+vite 빌드 통과, 54000 포트 새 번들(index-Bn6Gs0g2.js) 서빙
|
||||
- 카메라 감지 알고리즘을 실파일 앞 8MB로 재현 검증: ZenmuseP1 / 29.9mm / 3840×2160@59.94 정확 추출
|
||||
|
||||
## 다음 단계 참고
|
||||
|
||||
- **이미 캘리브레이션이 저장된 데이터셋(이번 세션에서 슬라이더 만진 경우)은 focal 자동 적용이
|
||||
건너뛰어짐** → 카메라 파라미터 패널의 [적용] 버튼으로 29.9mm 반영 필요
|
||||
- djmd 6.x(위경도 라디안/타원체고)는 CSV보다 정밀한 포즈 소스 — 향후 CSV 대체 후보
|
||||
- 8.8(변화 필드)은 포커스 거리로 추정 — 미사용
|
||||
@@ -0,0 +1,30 @@
|
||||
# 2026-07-09 카메라 focal 자동 적용 조건 개선
|
||||
|
||||
**소요 시간**: 4분
|
||||
**Context 사용량**: input 460k / output 3k tokens
|
||||
|
||||
## 문제
|
||||
|
||||
"드론 카메라 속성이 카메라 파라미터에 적용된 건가?" 확인 요청.
|
||||
직전 구현은 "저장된 보정(localStorage calib)이 있으면 자동 적용 건너뜀"이었는데,
|
||||
**보정 자동저장은 폴더를 열기만 해도 기본 params 로 저장**되므로(800ms 디바운스 저장 effect가
|
||||
첫 로드에도 실행) 제주 데이터셋은 이미 calib 이 존재 → 감지 focal(29.9mm)이 적용되지 않았을
|
||||
가능성이 높았음.
|
||||
|
||||
## 수정 — StationOverlay.tsx 자동 적용 조건
|
||||
|
||||
- 기존: 저장된 calib 존재 → 무조건 건너뜀
|
||||
- 변경: **사용자가 f 를 실제로 튜닝한 경우(저장된 f ≠ 기본값 24)만 유지**, 그 외
|
||||
(첫 로드 / 저장은 됐지만 f 기본값 그대로)는 감지값 자동 적용
|
||||
- setParams 함수형 업데이트 안에서 판단 → calib 복원값 기준으로 정확히 비교
|
||||
|
||||
## 확인 방법 (사용자 안내)
|
||||
|
||||
- 새로고침 → 폴더 재선택 → 카메라 파라미터 패널: **f = 29.9mm** 이고
|
||||
📷 줄에 "DJI ZenmuseP1 · 29.9mm" 표시되면 적용된 것
|
||||
- 콘솔 로그: `[camera] DJI ZenmuseP1 감지 → focal 29.9mm 자동 적용`
|
||||
- f 를 이미 직접 튜닝했던 경우엔 유지되며, [적용] 버튼으로 감지값 교체 가능
|
||||
|
||||
## 검증
|
||||
|
||||
- tsc+vite 빌드 통과, 54000 포트 새 번들(index-Ba4WSfaE.js) 서빙
|
||||
@@ -0,0 +1,33 @@
|
||||
# 2026-07-09 스테이션바 노선 밖 구간 진행 정지 수정
|
||||
|
||||
**소요 시간**: 4분
|
||||
**Context 사용량**: input 500k / output 3k tokens
|
||||
|
||||
## 문제 (스크린샷 제보)
|
||||
|
||||
드론이 전진 중인데(영상 1:17, GPS 33.4084/126.2945) 하단 스테이션바 커서·배지(0k000)가 고정.
|
||||
|
||||
## 원인
|
||||
|
||||
측점이 로드되면서 스테이션바 이동거리축이 **측점 폴리라인 체이니지 투영 |Δ| 누적** 방식으로
|
||||
동작하는데, 제주 비행은 **노선 시작점(0k000)에서 남서쪽 약 700m 밖에서 이륙해 접근**한다
|
||||
(0k000 도달은 87.3초 시점). 노선 밖에서는 투영이 폴리라인 끝점(0k000)에 클램프 → 체이니지
|
||||
변화량 0 → 접근 구간 내내(첫 87초) 커서·배지 정지.
|
||||
|
||||
## 수정 — client/src/stationbar/StationBar.tsx
|
||||
|
||||
- 이동거리축(frac)을 체이니지 |Δ| 대신 **GPS 실이동거리 누적**(±8프레임 이동평균 평활)으로 변경
|
||||
- 호버 시 정체(커서 정지) 특성 유지 + 노선 안팎 무관하게 실제 이동 반영
|
||||
- 측점 배지(최근접 측점)·구간 방향색(depChain/arrChain)은 기존 체이니지 기준 유지
|
||||
- 측점 없는 데이터셋용 GPS 폴백(직전 작업)과 계산 방식 일원화
|
||||
|
||||
## 검증 (실데이터 시뮬레이션, 세그먼트 1)
|
||||
|
||||
- 스크린샷 시점 t=77.8s: 이동 719m → 커서 37.7% (기존 방식은 0% 고정)
|
||||
- 총 이동거리 1909m, 0k000 도달 87.3s — 도달 전 배지가 0k000인 것은 정상(최근접 측점)
|
||||
- tsc+vite 빌드 통과, 54000 포트 새 번들(index-C1t_EPA4.js) 서빙
|
||||
|
||||
## 다음 단계 참고
|
||||
|
||||
- 배지는 "가장 가까운 측점"이라 노선 접근 전에는 0k000 고정이 맞는 동작 — 접근 구간을
|
||||
별도 표시(예: "노선 진입 전")하고 싶으면 chain 클램프 여부로 판별 가능
|
||||
@@ -0,0 +1,40 @@
|
||||
# 2026-07-09 스테이션바 원거리 구조물 마크 겹침 수정
|
||||
|
||||
**소요 시간**: 6분
|
||||
**Context 사용량**: input 540k / output 3k tokens
|
||||
|
||||
## 문제 (스크린샷 제보)
|
||||
|
||||
스테이션바에 교량 마크 2개가 붙어 있고 라벨이 "귀어음천교"처럼 보임 — 실제로는
|
||||
**"귀덕천교"와 "어음천교" 라벨이 같은 위치에 겹쳐** 찍힌 것. (귀어음천교라는 시설물은 없음)
|
||||
|
||||
## 원인
|
||||
|
||||
지장물 KML의 교량 3개 통과 시점(실데이터 계산):
|
||||
|
||||
| 교량 | 세그1 최근접 | 실제 통과 |
|
||||
|------|------------|----------|
|
||||
| 귀덕천교 | t=194.6s, 24m | 세그먼트 1 끝 (정상) |
|
||||
| 금성천교 | t=197.1s(끝 클램프), **1358m** | 세그먼트 3 (t=510.8s, 3m) |
|
||||
| 어음천교 | t=197.1s(끝 클램프), **1880m** | 세그먼트 3 (t=565.2s, 59m) |
|
||||
|
||||
`pxPassesTo`의 통과 인정 거리(TH)가 `max(100, 최근접거리×3)` — 근접 통과가 없는 구조물은
|
||||
TH 가 수 km 로 늘어나 전체 비행이 "통과"로 인정 → 최근접 프레임(세그먼트 끝)에 마크 생성 →
|
||||
세그1 끝에서 실제 통과하는 귀덕천교와 같은 px 에 3개 마크·라벨이 겹침.
|
||||
|
||||
## 수정 — client/src/stationbar/StationBar.tsx
|
||||
|
||||
- `pxPassesTo`에 근접 통과 상한 `PASS_MAX_M = 300` 추가: 이 영상(세그먼트)에서 300m 이내로
|
||||
접근하지 않은 구조물은 마크 생략. 해당 구조물은 실제 통과하는 세그먼트의 바에 정상 표시됨
|
||||
(세그먼트 전환 시 frames 교체 → 마크 재계산)
|
||||
|
||||
## 검증
|
||||
|
||||
- 실데이터 기준: 세그1 → 귀덕천교만(24m ✓), 금성·어음 제외(1358/1880m) ✓
|
||||
세그3 → 금성천교(3m)·어음천교(59m) 표시 ✓
|
||||
- tsc+vite 빌드 통과, 54000 포트 새 번들(index-CFrrDGW7.js) 서빙
|
||||
|
||||
## 다음 단계 참고
|
||||
|
||||
- PASS_MAX_M(300m)은 역사처럼 노선 옆 구조물도 포함하도록 여유 있게 설정 — 도로/철도
|
||||
공통으로 문제없을 값. 특수 데이터셋에서 조정 필요 시 route.json 파라미터화 검토
|
||||
@@ -0,0 +1,38 @@
|
||||
# 2026-07-09 스테이션 배지 노선 밖(접근 구간) 표기
|
||||
|
||||
**소요 시간**: 6분
|
||||
**Context 사용량**: input 580k / output 3k tokens
|
||||
|
||||
## 문제 (스크린샷 제보)
|
||||
|
||||
영상 54.8초(커서는 정상 진행 중)인데 스테이션 배지가 "0k000"에서 안 변함.
|
||||
|
||||
## 원인 (버그 아님 — 표기 문제)
|
||||
|
||||
이 비행은 노선 시작점(0k000)에서 남서쪽 ~700m 밖에서 이륙해 **87초에 노선 진입**.
|
||||
진입 전에는 "가장 가까운 측점 = 0k000"이라 배지가 0k000으로 고정되는 게 계산상 맞지만,
|
||||
사용자는 고장으로 인식 → 접근 구간임을 배지에 드러내기로 함.
|
||||
|
||||
## 수정 — client/src/stationbar/StationBar.tsx
|
||||
|
||||
- `projectChainageDist` 신설: 체이니지 + **폴리라인까지 수평거리(distM)** 반환
|
||||
(기존 `projectChainage`는 래퍼로 유지 — 구조물 마크 등 기존 호출부 무변경)
|
||||
- `ViewedPoint.offM` 추가(프레임별 노선 이탈 거리), `realOffM` memo 추가
|
||||
- 배지 표기: 노선에서 **60m 이상 벗어난 구간은 "노선밖 ○○m"** 로 표시(거리 실시간 갱신),
|
||||
60m 이내면 기존 체이니지(0k000, 0k110, …) 표시
|
||||
|
||||
## 검증 (실데이터 시뮬레이션, 세그먼트 1)
|
||||
|
||||
| 시점 | 배지 |
|
||||
|------|------|
|
||||
| 10s | 노선밖 708m |
|
||||
| 54.8s (스크린샷) | 노선밖 346m |
|
||||
| 87s (진입) | 0k000 |
|
||||
| 100s / 130s / 190s | 0k110 / 0k410 / 1k010 |
|
||||
|
||||
→ 진입 후 배지 정상 진행 확인. tsc+vite 빌드 통과, 새 번들(index-DyILp3c8.js) 서빙.
|
||||
|
||||
## 다음 단계 참고
|
||||
|
||||
- OFF_ROUTE_M=60m 고정 — 드론이 노선 옆으로 크게 이격 비행하는 데이터셋이면 route.json
|
||||
파라미터화 검토
|
||||
@@ -0,0 +1,47 @@
|
||||
# 2026-07-09 카메라 JSON 저장/적용 + 세그먼트 라벨 캐시 + 팝업 정렬 + 박스 높이
|
||||
|
||||
**소요 시간**: 20분
|
||||
**Context 사용량**: input 640k / output 10k tokens
|
||||
|
||||
## 요청 4건 처리
|
||||
|
||||
### 1. 카메라 화각 정보를 영상 이름의 JSON 으로 서버 저장 → 폴더에 함께 있으면 자동 적용
|
||||
|
||||
- **서버**: `routes/camera.ts` 신규 — `PUT /api/camera/:videoId` 가 영상 폴더(VIDEOS_DIR)에
|
||||
`<영상 base>.camera.json` 저장(`{ camera: {...}, model, savedAt }`), `GET` 조회. Path traversal 방어
|
||||
- **클라이언트**:
|
||||
- `parseCameraJson`(geoData): 폴더에서 `<영상 base>.camera.json`(또는 `<base>.json`) 인식 —
|
||||
폴더 내 어떤 분할본 base 와도 매칭, 숫자 필드만 통과
|
||||
- geoStore `cameraJson` 적재 → StationOverlay 에서 **최우선 적용**(localStorage 보정·djmd 감지값보다 위,
|
||||
effect 선언 순서로 보장)
|
||||
- 카메라 파라미터 패널 📷 줄에 **[서버에 저장]** 버튼 — 현재 파라미터 전체(focalLen/yaw/pitch/…)를
|
||||
현재 재생 중인 영상 이름으로 저장. 저장중…/저장됨 ✓/실패 상태 표시
|
||||
- E2E 검증: PUT → D: 폴더에 파일 생성 → GET 일치 → 정리 완료
|
||||
|
||||
### 2. 두 번째 영상에서 시설물 미표출
|
||||
|
||||
- 원인: 라벨 사전계산(startLabelPrecompute) effect deps 에 `storeFrames` 누락 —
|
||||
세그먼트 전환(loadSegment)으로 frames 가 교체돼도 이전 세그먼트의 라벨 캐시를 계속 사용 →
|
||||
새 위치에서 전부 화면 밖 투영
|
||||
- 수정: 두 precompute effect deps 에 `storeFrames` 추가 → 세그먼트 전환 시 재계산
|
||||
|
||||
### 3. 시설물 라벨 아래 검은 팝업 정렬
|
||||
|
||||
- 원인: 팝업 x 앵커가 구 라벨 레이아웃(지점 오른쪽 텍스트) 기준 `sx − 10` 고정 —
|
||||
라벨이 중앙정렬 박스로 바뀌면서 어긋남
|
||||
- 수정: POI 팝업은 `sx − 팝업폭/2`(박스 중앙 아래 정렬), 측점 팝업은 기존 유지
|
||||
|
||||
### 4. 라벨 배경 박스 여유
|
||||
|
||||
- BOX_H 22 → **24**(글자 20px × 1.2), PAD_X 6→7, 동일좌표 다중 라벨 행 간격 ROW_H 24→28
|
||||
|
||||
## 검증
|
||||
|
||||
- 클라이언트(tsc+vite)·서버(tsc) 빌드 통과, pm2 재시작, 새 번들(index-BxAoyQvu.js) 서빙
|
||||
- 카메라 API 왕복 테스트 통과
|
||||
|
||||
## 다음 단계 참고
|
||||
|
||||
- camera.json 저장은 서버 VIDEOS_DIR 기준 — 폴더 선택으로 여는 로컬 폴더가 VIDEOS_DIR 과
|
||||
다른 경로면 저장 파일이 그 폴더에 없음(현 구성은 동일 폴더라 문제없음)
|
||||
- camera.json 은 폴더 공용(분할본 전체 적용). 세그먼트별 별도 카메라가 필요하면 확장 필요
|
||||
@@ -0,0 +1,17 @@
|
||||
# 2026-07-09 스테이션바 커서 배지 줄바꿈 수정
|
||||
|
||||
**소요 시간**: 3분
|
||||
**Context 사용량**: input 680k / output 1k tokens
|
||||
|
||||
## 문제
|
||||
|
||||
커서 배지의 "노선밖 737m" 표기가 두 줄로 꺾여 표시됨 (배지가 min-width 112px + 20px 글자
|
||||
+ 줄바꿈 허용이라 공백에서 개행).
|
||||
|
||||
## 수정 — TimelineCursor.module.scss
|
||||
|
||||
- 배지 글자 20px → **17px**, `white-space: nowrap` 추가 (폭은 내용에 맞게 자동 확장)
|
||||
|
||||
## 검증
|
||||
|
||||
- 빌드 통과, 새 번들(index-Cf5yoZh9.js) 서빙
|
||||
@@ -0,0 +1,35 @@
|
||||
# 2026-07-09 호버 구간 라벨 깜빡임 수정 (세그먼트 2)
|
||||
|
||||
**소요 시간**: 6분
|
||||
**Context 사용량**: input 720k / output 3k tokens
|
||||
|
||||
## 문제
|
||||
|
||||
두 번째 영상 재생 시 라벨이 심하게 깜빡임.
|
||||
|
||||
## 원인
|
||||
|
||||
세그먼트 2는 **120초 호버 구간**(짐벌 회전 0.0°/s — 실데이터 확인, 완전 정지 프레이밍):
|
||||
|
||||
1. **화면 경계 걸침 라벨의 포함 경계 플래핑**: 라벨 사전계산의 화면 포함 판정이
|
||||
POI ±0.02 / 측점 ±0.05 로 타이트 — 호버 중 GPS 미세 지터로 경계에 걸친 라벨이
|
||||
캐시 프레임(10Hz)마다 포함↔제외를 반복 → 표시/소멸 깜빡임.
|
||||
(이동 중에는 경계 통과가 순간이라 눈에 안 띄었음 — 호버에선 120초 내내 반복)
|
||||
2. **겹침 억제 승자 플래핑**: 겹친 라벨 쌍의 우선순위가 dist 정렬 — 거리가 거의 같은 쌍은
|
||||
지터로 순위가 프레임마다 뒤바뀌어 두 라벨이 교대로 깜빡임.
|
||||
|
||||
## 수정 — client/src/components/overlay/StationOverlay.tsx
|
||||
|
||||
- 사전계산 포함 경계를 POI/측점 모두 **±0.15** 로 확대 — 경계 판정을 안정화하고
|
||||
실제 화면 밖 컬링은 드로우 단계(labelOnScreen)가 담당 (마커 수 증가는 미미)
|
||||
- 겹침 억제 정렬에 **거리차 1m 미만 = 제목순 고정** 타이브레이크 추가 — 승자 고정
|
||||
|
||||
## 검증
|
||||
|
||||
- 세그먼트별 짐벌 데이터: 세그1 평균 0.4°/s(이동 촬영) vs 세그2 평균 0.0°/s(호버) — 원인 부합
|
||||
- tsc+vite 빌드 통과, 새 번들(index-pDBh9-ym.js) 서빙
|
||||
|
||||
## 다음 단계 참고
|
||||
|
||||
- 남을 수 있는 잔여 깜빡임: POI 표시범위(maxPoiRange) 경계(기본 1000m)에 정확히 걸친 POI 가
|
||||
호버 중 지터로 in/out 반복하는 경우 — 발생 시 범위 판정에 히스테리시스(±2%) 추가 검토
|
||||
@@ -0,0 +1,23 @@
|
||||
# 2026-07-09 카메라 패널 값 출처 확인 (Q&A)
|
||||
|
||||
**소요 시간**: 3분
|
||||
**Context 사용량**: input 750k / output 1k tokens
|
||||
|
||||
## 질문
|
||||
|
||||
폴더 로드 후 카메라 파라미터 패널 값들이 드론에 저장된 카메라 값인지 확인 요청.
|
||||
|
||||
## 답변 (코드 변경 없음)
|
||||
|
||||
| 항목 | 값 | 출처 |
|
||||
|------|-----|------|
|
||||
| f | 29.9mm | **드론(djmd) 감지값 자동 적용** ✓ |
|
||||
| 상태줄 yaw/pitch | 13.2°/−15° | **드론 비행로그**(현재 프레임 짐벌 자세) ✓ |
|
||||
| Yaw ± | −16.5° | localStorage 복원된 사용자 보정(기본 0) — f=24 시절 튜닝 잔재, 재조정 권장 |
|
||||
| cy₀ | −0.105 | localStorage 복원된 사용자 보정(기본 0) |
|
||||
| 지오이드 | 25.8 | 앱 기본값(대전 기준) — 지면고도 모드에선 미사용 |
|
||||
| sen W/H | 36/20.25 | 앱 기본값 — P1이 실제 풀프레임이라 사실과 일치 |
|
||||
|
||||
## 사용자 안내
|
||||
|
||||
초기화 → f 29.9 적용 → Yaw 재조정 → [서버에 저장]으로 camera.json 고정 권장.
|
||||
@@ -0,0 +1,28 @@
|
||||
# 2026-07-09 djmd에 yaw/cy₀ 존재 여부 확인 (Q&A)
|
||||
|
||||
**소요 시간**: 5분
|
||||
**Context 사용량**: input 780k / output 2k tokens
|
||||
|
||||
## 질문
|
||||
|
||||
드론 카메라에서 yaw, cy₀ 값도 가져올 수 있지 않은지.
|
||||
|
||||
## 분석 (코드 변경 없음)
|
||||
|
||||
세그먼트1 중반(프레임 5990, t≈100s, CSV yaw=65.1°) djmd 레코드 전수 해독:
|
||||
|
||||
- 존재: 위치(6.4/6.5 위경도 라디안, 6.6 타원체고 mm), 노출(7.4 셔터분모 60~65, 7.5 ISO계열,
|
||||
7.7~7.10 측광), 렌즈(8.6 초점 299=29.9mm, 8.8 포커스거리), 해상도/fps
|
||||
- **부재: 짐벌 자세(yaw/pitch/roll), 주점(cx/cy) 캘리브레이션**
|
||||
- 7.4=65 는 yaw(65.1°)와 우연히 유사하나 프레임10에서 61(당시 yaw 29.7°)로 불일치 → 셔터 분모로 판정
|
||||
|
||||
## 답변 요지
|
||||
|
||||
- Yaw±는 로그 계통 오차(자기편각 등)의 보정 델타 — 드론 데이터에 존재 불가
|
||||
- cy₀는 DJI **사진 JPG의 XMP DewarpData**에만 존재 (fx/fy/cx/cy/왜곡계수)
|
||||
|
||||
## 제안 (미구현)
|
||||
|
||||
1. P1 사진 1장 폴더 동봉 시 DewarpData 자동 적용 기능
|
||||
2. GPS 진행방위 vs 짐벌 헤딩으로 yaw 바이어스 자동 추정
|
||||
3. 라벨 드래그로 yaw 역산 모드 (FOV 보정과 동일 패턴) — 실무적 최선
|
||||
@@ -0,0 +1,15 @@
|
||||
# 2026-07-09 Yaw 보정 방안 2·3번 상세 설명 (Q&A)
|
||||
|
||||
**소요 시간**: 3분
|
||||
**Context 사용량**: input 800k / output 1k tokens
|
||||
|
||||
## 내용 (코드 변경 없음)
|
||||
|
||||
Yaw 보정 자동화 두 방안을 쉬운 언어로 재설명:
|
||||
|
||||
- **2번(자동 추정)**: GPS 이동 방위(정확) vs 짐벌 헤딩(나침반 오차 포함)의 일정한 차이의
|
||||
중앙값을 Yaw± 에 자동 입력. 버튼 한 번, 근사치. 카메라가 진행방향을 안 본 구간이 섞이면 오차.
|
||||
- **3번(드래그 역산)**: 일시정지 → 어긋난 라벨을 영상 속 실제 지물 위치로 드래그 →
|
||||
필요한 Yaw± 를 역산해 자동 설정. 기존 FOV 보정 모드(f 역산)와 동일 패턴. 정확·직관적.
|
||||
|
||||
추천: 3번(정확) 또는 2번+3번 병행. 사용자 선택 대기.
|
||||
@@ -0,0 +1,39 @@
|
||||
# 2026-07-09 Yaw 자동추정 + 드래그 역산 + 카메라값 PC 저장
|
||||
|
||||
**소요 시간**: 15분
|
||||
**Context 사용량**: input 850k / output 8k tokens
|
||||
|
||||
## 작업 내용 (요청 3건)
|
||||
|
||||
### 1. Yaw 자동 추정 버튼 (2번 방안)
|
||||
|
||||
- 카메라 파라미터 패널 'Yaw ±' 아래 **[Yaw 자동추정 (비행경로 기준)]** 버튼
|
||||
- 알고리즘: 전진 구간(속도≥2m/s)에서 GPS 진행 방위(1초 창 평활) − 짐벌 헤딩 차이 수집
|
||||
(|차|≥60° 는 옆보기 촬영으로 간주 제외) → **중앙값을 Yaw± 로 설정**
|
||||
- 실데이터 검증: 세그먼트1 유효 샘플 1889개, 중앙값 **−16.3°**
|
||||
— 사용자가 이전에 수동 튜닝했던 −16.5° 와 거의 일치 (알고리즘·기존 튜닝 상호 검증)
|
||||
|
||||
### 2. Yaw 드래그 역산 모드 (3번 방안)
|
||||
|
||||
- POI 편집 섹션에 **[🧭 Yaw 보정 — POI를 실제 위치(좌우)로 드래그]** 모드 추가
|
||||
(편집/세로화각 모드와 상호 배제, 기존 fovMode 패턴 재사용)
|
||||
- 역산: θ0=atan2(Xc,Zc)(현재 투영각) vs θ1=atan((px*−0.5−cx0)·sW/f)(드롭 지점 요구각)
|
||||
→ `yawOffset += (θ0−θ1)`. |Δ|>45° 는 오조작으로 거부
|
||||
- 게이팅(커서/히트/팝업/드래그 피드백) 4곳에 yawMode 연결
|
||||
|
||||
### 3. 카메라값 저장: 서버 → **사용자 PC 다운로드**
|
||||
|
||||
- 📷 줄 버튼 '서버에 저장' → **[PC에 저장]**: 현재 파라미터 전체(Yaw± 보정 결과 포함)를
|
||||
`<영상이름>.camera.json` 으로 브라우저 다운로드
|
||||
- 받은 파일을 영상 폴더에 넣으면 폴더 선택 시 최우선 자동 적용(기존 로더 그대로)
|
||||
- 서버 라우트(/api/camera)는 유지하되 UI 에서는 미사용
|
||||
|
||||
## 검증
|
||||
|
||||
- tsc+vite 빌드 통과, 새 번들(index-D5ltlp17.js) 서빙
|
||||
- Yaw 자동 추정 실데이터 시뮬레이션 −16.3° (수동값 −16.5° 부합)
|
||||
|
||||
## 사용 흐름 (사용자 안내)
|
||||
|
||||
1. [Yaw 자동추정] → 대략 정렬 2. [🧭 Yaw 보정] 모드에서 라벨 하나를 실제 지물로 드래그 → 미세 조정
|
||||
3. [PC에 저장] → 받은 `<영상이름>.camera.json` 을 영상 폴더로 이동 → 이후 폴더 선택만으로 적용
|
||||
Reference in New Issue
Block a user