기존 GhiVideo 저장소 HEAD의 트래킹 소스 362개 파일을 복제. (node_modules·storage·빌드 산출물·대용량 미디어는 .gitignore 규칙대로 제외) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
11 KiB
11 KiB
데이터 폴더 입력 명세 — 스테이션기반 플레이어
웹플레이어가 동영상이 있는 폴더를 전달받아 재생할 때, 폴더 안에 어떤 파일이 어떤 형식으로 있어야 하는지를 정의한다. 근거:
client/src/utils/geoData.ts(폴더 파서) 기준. 실데이터(v3.0, 대전조차장–회덕)로 검증.
1. 동작 개요
- 사용자가 브라우저에서 폴더를 선택하면(
<input type="file" webkitdirectory>), 폴더 내 전체 파일 목록이 플레이어에 전달된다. - 플레이어는 파일명 규칙으로 각 파일을 자동 식별·파싱한다(별도 설정 불필요).
- 기준 이름 base = 영상 파일명에서 확장자를 뺀 것 (예:
상행)대전조차장-회덕.MP4→ base는상행)대전조차장-회덕). 나머지 파일은 base 일치를 우선 매칭하고, 없으면 폴백 규칙으로 찾는다.
2. 폴더 구성 요약
| # | 파일 | 필수 | 역할 |
|---|---|---|---|
| 1 | <base>.MP4 |
필수 | 드론 촬영 항공영상 (mp4/webm) |
| 2 | <base>.csv |
필수 | 드론 비행로그 — 프레임별 위치·자세각·초점거리 |
| 3 | 01)측점.csv 또는 측점.csv |
필수 | 측점 목록(이정·좌표·실측 표고) → 중심선·스테이션바·POI 높이 보완의 근간 |
| 4 | <base>.kmz (또는 .kml) |
필수(정책) | POI·구조물의 유일한 소스 (지장물·교량·터널·출입문·구교·철도역) |
| 5 | <base>.route.json 또는 route.json |
선택 | 노선 기본 정보 + 구조물 위치 보정 |
| 6 | <base>_poi_overrides.json |
선택 | POI 드래그 보정 저장값 (역투영 결과) |
| — | .srt, .qgz, .qgs 등 그 외 파일 |
무시 | 플레이어가 읽지 않음 (작업용 부산물) |
예시 폴더 (실데이터 v3.0):
상행)대전조차장-회덕 데이터(v3.0)/
├── 상행)대전조차장-회덕.MP4 ← 영상 (base 기준)
├── 상행)대전조차장-회덕.csv ← 드론 비행로그
├── 01)측점.csv ← 측점 (루트 또는 building/ 하위 모두 허용)
├── 상행)대전조차장-회덕.kmz ← POI·구조물 원본
├── 상행)대전조차장-회덕.route.json ← 노선 정보 (선택)
├── 상행)대전조차장-회덕.srt ← (무시됨)
└── 상행)대전조차장-회덕.qgz ← (무시됨, QGIS 작업파일)
3. 파일별 상세 명세
3.1 영상 — <base>.MP4
- 확장자:
.mp4또는.webm(대소문자 무관).building/하위가 아닌 루트의 첫 번째 영상 파일을 사용한다. - 코덱: 브라우저 직접 재생이므로 H.264 권장.
- 용량: 2GB 이상 가능 (
URL.createObjectURL()참조 방식 — 메모리에 통째로 올리지 않음). - 이 파일명이 폴더 전체의 base 를 결정한다.
3.2 드론 비행로그 — <base>.csv
드론이 촬영과 동시에 기록한 프레임별 데이터. 헤더 이름으로 컬럼을 찾으며(순서 무관), 헤더가 깨졌을 때만 위치 인덱스로 폴백한다.
| 컬럼(헤더명) | 의미 | 단위/규약 |
|---|---|---|
frame_cnt |
프레임 번호 (0부터) | 정수 |
latitude / longitude |
드론 위치 위도/경도 | 도(WGS84) |
altitude |
드론 고도 | m (타원체고) |
yaw |
기수 방위각 | 도 — 북=0, 시계방향 |
pitch |
카메라 틸트 | 도 — 음수=아래 |
roll |
롤 | 도 |
focal_len |
초점거리 | mm (35mm 환산) |
- 추가 컬럼(
rel_alt,5186x,5186y,X좌표,Y좌표등)이 있어도 무시되고 오류 없음. latitude가 숫자가 아닌 행은 건너뜀.- 파일 선택 규칙: 루트의
.csv중 ①<base>.csv정확 일치 우선, ② 없으면 파일명에POI가 없는 첫.csv(영상명 비의존 폴백). - fps 자동 산출: 최대
frame_cnt÷ 영상 길이(초) → 표준 fps(24/25/29.97/30/50/60…)에 스냅. 따라서 비행로그의 프레임 수는 영상의 실제 프레임 수와 일치해야 한다.
3.3 측점 — 01)측점.csv (또는 측점.csv)
철도 노선 측량 성과. 시간↔측점 대응표(스테이션바)와 선로 중심선, POI 높이 보완의 근간 데이터.
- 파일 찾기 규칙 (3단 폴백): ①
building/하위에서 파일명에01)측점포함 → ② 루트 포함 아무 위치에서01)측점포함 → ③ 파일명에측점포함. - 컬럼 (헤더 이름 기준):
| 컬럼 | 필수 | 의미 |
|---|---|---|
측점 |
필수 | 이정 표기 (예 157K900) — 스테이션 정렬·표시 기준 |
lat / lon |
필수 | 측점 좌표 (도, WGS84) — 숫자 아니면 해당 행 제외 |
Z좌표_한국 |
권장 | 실측 정표고(EL, m) — 있으면 최우선 사용 |
Z좌표 |
폴백 | Z좌표_한국 이 없을 때 사용 |
비고 |
선택 | 방향전환점(상행->하행, 02:05) 패턴이 있으면 방향전환점으로 추출 |
- 중심선 자동 생성: v2.0 이후
center.csv가 없으므로, 측점을 이정 순으로 이어 만든 폴리라인이 선로 중심선이 된다. → 측점 정확도가 곧 측점 투영·POI 높이 보완의 정확도.
⚠️ 주의 (실데이터에서 발견): 상행 v3.0의
01)측점.csv는 실측 표고가z컬럼에 있고Z좌표컬럼은 0.0 인데, 파서는Z좌표_한국→Z좌표순서로만 읽으므로 표고가 0으로 로드된다. 하행 파일처럼 표고 컬럼명을Z좌표_한국으로 통일하거나, 파서에z컬럼 폴백 추가가 필요하다.
3.4 POI·구조물 — <base>.kmz (또는 .kml)
POI·구조물의 유일한 소스다 (building/ 하위 02)~06) CSV는 더 이상 읽지 않음). 루트의 bare .kml 이 있으면 우선, 없으면 .kmz(zip)를 해제해 doc.kml 을 읽는다.
- KML 폴더 구조 → 분류 매핑:
| KML 폴더명 (포함 키워드) | 분류 | 이름 필드 (description 표) |
|---|---|---|
03)교량 |
구조물 bridge | 구분 (+ 연장(m), 시설종별) |
04)터널 |
구조물 tunnel | 구분 (+ 연장(m), 시설종별) |
06)구교 |
구조물 bridge(구교) | 시설물명 (+ 연장(m)) |
05)출입문번호 |
POI (출입문) | 출입문번호 |
02)지장물 등 그 외 |
POI (지장물 등) | 구분 → title → placemark name 순 |
— source=KAKAO_RAIL 또는 category_clean=철도역/역사 |
POI + 역사 구조물 (스테이션바에도 표출) | 위와 동일 |
- Placemark 좌표:
<coordinates>의lon,lat[,alt]우선. 없으면 description 속성lat/lon. 한국 범위 검증(위도 3339, 경도 124132) 밖이면 버림. - 속성(description): HTML
<table>의 (키, 값) 행으로 기재 —구분,연장(m),시설종별(1종/2종/3종/기타),시설물명,출입문번호,source,category_clean,title, 표고(z/Z좌표/절대고도) 등. 구글어스 재저장본의 표 변형(<tbody>, 엔티티 등)도 허용. - 표고: POI 속성에
z/절대고도가 있으면 사용, 없으면 0 → 재생 시 최근접 측점 실측 표고로 보완된다. - 누락 시: 경고 로그 후 POI·구조물 없이 동작(측점·드론·영상은 정상). CSV 폴백은 없다.
3.5 노선 기본 정보 — <base>.route.json (선택)
루트의 <base>.route.json 우선, 없으면 route.json. UTF-8 JSON.
{
"routeInfo": {
"name": "대전조차장-회덕", // 노선명 (표시용)
"direction": "상행", // 방향 라벨
"lengthKm": 4.25, // 구간 연장 (km)
"durationSec": 568, // 영상 길이 (초)
"startStationName": "대전", // 시점역
"endStationName": "신탄진", // 종점역
"stationTolerance": 20, // 측점 인정 반경(m) — 기본 40
"endStationGapMeters": 0 // 종점 미도착 거리(m) — 0이면 도착
},
"structures": [ // (선택) 구조물 보정 — name 매칭으로 override
{
"name": "회덕천교", // 괄호 이하 제거 후 부분일치 매칭
"station": "158k400", // 최우선: 이 측점값 위치에 배치 (숫자 m 또는 "158k400")
"offset": -10, // 진행방향 ±N m 미세조정
"startMileage": 158400, // 매칭 실패 시 폴백 이정
"endMileage": 158500,
"lat": 36.4031, "lon": 127.4216 // 좌표 직접 지정도 가능
}
]
}
structures항목은 KMZ 유래 구조물에 명시된 필드만 덮어쓰고, KMZ에 없는 항목은 새 구조물로 추가된다.
3.6 POI 위치 보정 — <base>_poi_overrides.json (선택)
영상에서 POI 라벨을 드래그(역투영) 해 보정한 좌표의 저장/배포 파일. 폴더에 있으면 로드 시 자동 적용된다.
{
"baseName": "상행)대전조차장-회덕",
"overrides": {
"회덕터널": { "lat": 36.40312, "lon": 127.42169, "z": 66.1 }
}
}
overrides없이 평면 맵({ "회덕터널": {…} })도 허용.z는 정표고(m). POItitle정확 일치로 매칭.
4. 공통 규칙 (인코딩·파싱)
| 항목 | 규칙 |
|---|---|
| CSV 인코딩 | 자동 감지 — 앞 3바이트가 UTF-8 BOM(EF BB BF)이면 UTF-8, 아니면 EUC-KR. 파일별 하드코딩 없음 |
| CSV 컬럼 | 헤더 이름으로 탐색 (순서 무관, 여분 컬럼 무시). 헤더 훼손 시 위치 인덱스 폴백 |
| CSV 형식 | 큰따옴표 필드 지원, 빈 줄 무시, \r\n/\n 모두 허용 |
| KML | UTF-8. .kml > .kmz(내부 doc.kml) 우선순위 |
| JSON | UTF-8. 파싱 실패 시 조용히 무시(선택 파일이므로) |
| 폴더 구조 | building/ 하위는 측점 CSV 탐색 외에는 사용하지 않음. 하위 폴더 깊이는 1단계(building/)만 인식 |
5. 파일 누락 시 동작
| 누락 파일 | 동작 |
|---|---|
| 영상 | 재생 불가 (폴더 인식 실패) |
| 드론 비행로그 | 측점 투영·POI 오버레이·fps 산출 불가 → 사실상 일반 플레이어로 전락 |
| 측점 CSV | 중심선·스테이션바·POI 높이 보완 불가 (시간축 재생만 가능) |
| KMZ/KML | 경고 로그 후 POI·구조물 미표시 (측점·드론·영상은 정상 동작) |
| route.json | 기본값으로 동작 (노선명 등 미표시, stationTolerance=40) |
| poi_overrides.json | 보정 없이 원본 좌표 사용 |
부록 — 구현 근거 (소스 위치)
| 항목 | 위치 |
|---|---|
| 폴더 통합 로더 | client/src/utils/geoData.ts — loadFolderGeoData() |
| 영상/base 식별 | 같은 파일 — findVideoFile(), deriveBaseName() |
| 드론 CSV 파서 | 같은 파일 — parseDroneFrames() |
| 측점 CSV 파서 (+방향전환점) | 같은 파일 — parseStations() |
| KMZ/KML 파서 | 같은 파일 — parseKmz() |
| route.json / 구조물 병합 | 같은 파일 — parseRouteMeta(), mergeStructures() |
| POI 보정 로드/적용 | 같은 파일 — parsePoiOverrides(), applyPoiOverrides() |
| 인코딩 자동 감지 | 같은 파일 — decodeBytes() |
| 중심선 생성 | 같은 파일 — buildCenterlineFromStations() |
| (참고) 서버 검색 API 용 데이터 | server/src/routes/geo.ts — 환경변수 GEO_DATA_DIR 폴더에 동일 형상 필요 |