기존 GhiVideo 저장소 HEAD의 트래킹 소스 362개 파일을 복제. (node_modules·storage·빌드 산출물·대용량 미디어는 .gitignore 규칙대로 제외) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4.9 KiB
2026-06-23 — 드론경로 평활·투명도 + 화면표시 옵션 패널 분리
소요 시간: 약 60분 Context 사용량: input ~140k / output ~14k tokens (추정)
배경 / 요청
QGIS 드론 궤적이 계단/Z로 보이는 원인 분석(= GPS 10Hz를 30fps에 복사·유지하여 생긴 계단 + 02:05 방향전환) 이후, 다음을 요청받음:
- 드론 경로를 실제 GPS 측정점(동그라미)+선으로 표시 → 표시했더니 먼 쪽에서 톱니로 튐(영상은 매끄러운데).
- 드론 경로에 투명도 추가.
- 선형(중심선) 과 드론 궤적 을 각각 독립 on/off.
- 카메라 파라미터를 제외한 모든 표시 설정을 "화면표시 옵션" 별도 버튼 → 팝업으로 분리.
1. 드론 경로 — 측정점 점+선 → 이동평균 평활
client/src/components/overlay/StationOverlay.tsx 캐시 빌드부.
문제
GPS 노이즈(±0.5m) + 10Hz 계단이 z=0(드론 58m 아래·지평선 쪽) 비스듬 투영에서 화면상 큰 톱니로 증폭. 실제 비행/영상은 매끄러움.
해결
경로점을 ±15프레임 이동평균(sm) 으로 펴서 투영. 5프레임마다 서브샘플, 점(dot)+선 동시 생성.
const BACK = 150, FWD = 1500, STEP = 5, PW = 15;
const sm = (i) => { // ±PW 프레임 lat/lon 평균
let la=0, lo2=0, n=0;
for (let k=max(0,i-PW); k<=min(len-1,i+PW); k++){ la+=frames[k].lat; lo2+=frames[k].lon; n++; }
return [la/n, lo2/n];
};
for (let i=lo; i<=hi; i+=STEP) {
const [la,lo2] = sm(i);
const cc = toCameraCoords(drone, la, lo2, Z, params, worldOrigin);
if (prevCc) { const s=segFrom(prevCc,cc); if (s) dronePathSegs.push(s); }
if (cc.Zc>=CLIP_Z) { const p=pixelFromCamera(cc,params); if(화면안) dronePathPts.push([p.pxRaw,p.pyRaw]); }
prevCc = cc;
}
RenderCache에 dronePathPts: [number,number][] 추가.
2. 투명도 (dronePathAlpha)
상태/ref 추가, 기본 0.55. RAF에서 선·점 색의 alpha에 적용.
const [dronePathAlpha, setDronePathAlpha] = useState(0.55);
const dronePathAlphaRef = useRef(0.55);
// RAF:
const dpa = dronePathAlphaRef.current;
ctx.strokeStyle = `rgba(80,200,255,${dpa})`; // 선
ctx.fillStyle = `rgba(255,230,90,${dpa})`; // 점
화면표시 옵션 팝업에 "투명도" 슬라이더(0.1~1.0).
3. 선형(중심선)·드론 궤적 독립 토글
기존: 중심선·드론경로·측점·POI 모두 visible(부모 측점선 토글)에 묶임 → 측점선 OFF면 전부 사라짐.
변경
showCenterline상태/ref 신설(기본 true).- 캐시 빌드 게이트:
!visible→!(visible || showCenterline || showDronePath)(effect deps에showCenterline추가). - RAF 그리기 분리:
(기존
if (cache) { if (showCenterlineRef.current) → 중심선 if (dronePathSegs/Pts) → 드론 경로 (showDronePath 시에만 캐시에 채워짐) if (visibleRef.current) → 측점/POI 라벨 }if (visibleRef.current && cache)한 덩어리를 위 3개로 분해. 라벨 블록을if(visibleRef.current){…}로 감싸고 닫는 중괄호 1개 추가.)
동작 변화(주의)
부모의 측점선 토글(visible) 은 이제 측점/POI 라벨만 제어. 중심선(선형)은 화면표시 옵션의 "선형(중심선) 표시" 토글(기본 ON)로 별도 제어. 드론 궤적도 측점선과 무관하게 단독 표시 가능.
4. 패널 분리 — 카메라 파라미터 / 화면표시 옵션
우측(나침반 아래) 버튼 2개:
- ▼ 화면표시 옵션(
showDisplay): 표시 토글(선형/드론궤적+표고+투명도), 스무딩(smooth/EMA), POI 필터/높이(앞·옆·드론고도비교), POI 위치 편집(편집모드/표고/내보내기·가져오기/DEM/보정초기화). - ▼ 카메라 파라미터(
showControls): 자세 보정(Yaw/Pitch/Roll), 위치 보정(offX/Y/Z·지오이드), 내부표정(f/cx₀/cy₀/senW/senH), 실시간 readout, params 초기화.
분류 기준: 드론 카메라 투영 파라미터 = 카메라 파라미터, 그 외 표시/필터/편집 = 화면표시 옵션.
const [showDisplay, setShowDisplay] = useState(false); 추가. 두 팝업은 독립 토글(동시 열림 가능, 각 max-h-[80vh] overflow-y-auto).
변경 파일
client/src/components/overlay/StationOverlay.tsx- 상태/ref:
showCenterline,dronePathAlpha,showDisplay RenderCache.dronePathPts- 캐시 빌드: 경로 평활(sm)+점 수집, 게이트
(visible||showCenterline||showDronePath) - RAF: 중심선/드론경로/라벨 독립 그리기 + 투명도
- 패널 JSX: 1개 → 화면표시 옵션 + 카메라 파라미터 2팝업
- 상태/ref:
빌드: index-BbLxqCEp.js ✓ (node20 npm run build -w client)
확인
Ctrl+Shift+R → 우측 나침반 아래 버튼 2개. "화면표시 옵션"에서 선형/드론궤적 독립 토글·투명도·표고 조절. "카메라 파라미터"는 투영 보정만.