Files
GhiVideo_v4/docs/쉬운설명_프레임기반에서_측점기반_플레이어.html
b23042andClaude Fable 5 d38b842e8d GhiVideo 소스 복제 — v4 작업 시작 기준
기존 GhiVideo 저장소 HEAD의 트래킹 소스 362개 파일을 복제.
(node_modules·storage·빌드 산출물·대용량 미디어는 .gitignore 규칙대로 제외)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 14:32:51 +09:00

340 lines
24 KiB
HTML

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang xml:lang>
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>쉬운설명_프레임기반에서_측점기반_플레이어</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
.display.math{display: block; text-align: center; margin: 0.5rem auto;}
</style>
<style type="text/css">@page {
size: A4;
margin: 18mm 16mm 16mm 16mm;
@bottom-center {
content: counter(page) " / " counter(pages);
font-family: "Malgun Gothic", sans-serif;
font-size: 9pt;
color: #999;
}
}
html { font-size: 11pt; }
body {
font-family: "Malgun Gothic", "Hancom Gothic", sans-serif;
color: #23272e;
line-height: 1.65;
max-width: 920px;
margin: 0 auto;
padding: 24px;
}
h1 {
font-size: 1.7rem;
color: #b45309;
border-bottom: 3px solid #f59e0b;
padding-bottom: 8px;
margin: 0 0 4px;
}
h2 {
font-size: 1.25rem;
color: #b45309;
border-bottom: 1px solid #e5d3b3;
padding-bottom: 5px;
margin-top: 1.6em;
}
h3 { font-size: 1.05rem; color: #92400e; margin-top: 1.1em; }
a { color: #b45309; }
hr { border: none; border-top: 1px solid #e2e2e2; margin: 1.6em 0; }
ul { padding-left: 1.25em; }
li { margin: 0.18em 0; }
strong { color: #1f2937; }
code {
font-family: "D2Coding", Consolas, monospace;
background: #f4f1ea;
border: 1px solid #e7e0d2;
border-radius: 3px;
padding: 0.5px 5px;
font-size: 0.92em;
}
table {
border-collapse: collapse;
width: 100%;
margin: 0.8em 0;
font-size: 0.95em;
}
th, td { border: 1px solid #d8d2c4; padding: 6px 10px; text-align: left; vertical-align: top; }
th { background: #fdf3df; color: #7c2d12; }
blockquote {
border-left: 4px solid #f59e0b;
margin: 0.8em 0;
padding: 0.2em 0 0.2em 14px;
color: #555;
background: #fffbf2;
}
h1, h2, h3 { break-after: avoid; }
</style>
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<header id="title-block-header">
<h1 class="title">쉬운설명_프레임기반에서_측점기반_플레이어</h1>
</header>
<h1 id="시간이-아니라-측점으로-움직이는-영상-플레이어-그림-설명서">시간이 아니라 &#39;측점&#39;으로 움직이는 영상 플레이어 (그림 설명서)</h1>
<blockquote>
<p>이 글은 <strong>초등학생도 이해할 수 있게</strong> 그림과 이야기로 쓴 설명서예요. &quot;보통 영상 플레이어는 <strong>시간(프레임)</strong> 으로 움직이는데, 우리는 어떻게 <strong>측점(선로 위치)</strong> 으로 움직이게 바꿨을까?&quot; 그 비밀을 하나씩 풀어 줄게요. (진짜 코드는 <a href="../client/src/utils/chainage.ts">chainage.ts</a> · <a href="../client/src/stationbar/StationBar.tsx">StationBar.tsx</a> 참고)</p>
</blockquote>
<style>
figure.fig{margin:22px 0;text-align:center;page-break-inside:avoid;break-inside:avoid;}
figure.fig svg{width:100%;max-width:720px;height:auto;border:1px solid #e7e0d2;border-radius:10px;background:#fffdf8;}
figure.fig figcaption{font-size:0.9em;color:#777;margin-top:6px;}
text{font-family:'Malgun Gothic','Hancom Gothic',sans-serif;}
h2{border-top:3px solid #e7e0d2;padding-top:14px;margin-top:34px;}
code{background:#f5f3ee;padding:1px 4px;border-radius:4px;}
</style>
<hr />
<h2 id="0-한-문장으로-말하면">0. 한 문장으로 말하면</h2>
<p><strong>&quot;영상의 매 장면마다 드론 GPS로 &#39;지금 몇 측점인지&#39;를 계산해 두어서, 시간(몇 분 몇 초)이 아니라 측점(160k130 같은 선로 위치)으로 영상을 찾아보게 만든 기술&quot;</strong> 이에요.</p>
<blockquote>
<p>🎯 비유: 보통 영화는 &quot;32분 10초로 가줘&quot;라고 하죠. 우리 플레이어는 <strong>&quot;회덕터널 앞으로 가줘&quot;</strong> 처럼 <strong>장소로</strong> 찾아가요.</p>
</blockquote>
<hr />
<h2 id="1-보통-플레이어-vs-우리-플레이어">1. 보통 플레이어 vs 우리 플레이어</h2>
<p>보통 영상 플레이어의 아래 막대는 <strong>시간</strong>(0:00 ~ 끝)이에요. 그런데 철도 점검하는 분들은 &quot;몇 분&quot;이 아니라 <strong>&quot;몇 측점&quot;</strong> 으로 이야기해요. 그래서 막대를 <strong>측점 기준</strong>으로 바꿨어요.</p>
<figure class="fig">
<svg viewBox="0 0 720 250" role="img" aria-label="시간축 재생바 vs 측점축 재생바 비교">
<text x="360" y="24" text-anchor="middle" font-size="13" font-weight="bold" fill="#475569">① 보통 플레이어 (시간 축)</text>
<rect x="40" y="38" width="640" height="26" rx="6" fill="#e5e7eb" stroke="#9ca3af"></rect>
<rect x="40" y="38" width="250" height="26" rx="6" fill="#9ca3af"></rect>
<circle cx="290" cy="51" r="10" fill="#374151"></circle>
<text x="45" y="82" font-size="11" fill="#374151">0:00</text>
<text x="300" y="82" font-size="11" fill="#374151">03:12</text>
<text x="648" y="82" font-size="11" fill="#374151">09:28</text>
<text x="360" y="104" text-anchor="middle" font-size="11" fill="#6b7280">&quot;3분 12초 지점&quot; — 여기가 어느 선로인지 모름 😵</text>
<text x="360" y="150" text-anchor="middle" font-size="13" font-weight="bold" fill="#166534">② 우리 플레이어 (측점 축)</text>
<rect x="40" y="164" width="640" height="26" rx="6" fill="#fde9c8" stroke="#e0a94f"></rect>
<rect x="40" y="164" width="310" height="26" rx="6" fill="#f59e0b"></rect>
<circle cx="350" cy="177" r="11" fill="#ea580c"></circle><text x="350" y="181" text-anchor="middle" font-size="9" fill="#fff" font-weight="bold">160k</text>
<line x1="205" y1="160" x2="205" y2="194" stroke="#0284c7" stroke-width="2"></line><text x="205" y="210" text-anchor="middle" font-size="9" fill="#0369a1">회덕터널</text>
<line x1="480" y1="160" x2="480" y2="194" stroke="#0284c7" stroke-width="2"></line><text x="480" y="210" text-anchor="middle" font-size="9" fill="#0369a1">회덕천교</text>
<text x="45" y="230" font-size="11" fill="#166534">대전조차장</text>
<text x="612" y="230" font-size="11" fill="#166534">신탄진</text>
</svg>
<figcaption>시간 축은 &quot;몇 분&quot;만 알려준다. 측점 축은 &quot;지금 어느 선로 위치(측점)&quot;, &quot;무슨 구조물 근처&quot;인지 한눈에 보인다.</figcaption>
</figure>
<hr />
<h2 id="2-측점이-뭐예요-">2. &#39;측점&#39;이 뭐예요? 📍</h2>
<p>철도에서 <strong>출발점부터 몇 미터 왔는지</strong>를 나타내는 &#39;거리 이정표&#39;예요. 도로의 <strong>km 표지판</strong>과 똑같아요!</p>
<ul>
<li><code>160k130</code> = 출발점에서 <strong>160km + 130m</strong> 지점</li>
<li>숫자가 커지면 앞으로 간 것, 작아지면 되돌아온 것</li>
</ul>
<hr />
<h2 id="3-핵심-비밀--매-장면마다-시간과-측점을-짝지어-둔다-️">3. 핵심 비밀 — 매 장면마다 &#39;시간&#39;&#39;측점&#39;을 짝지어 둔다 🗺️</h2>
<p>비밀은 <strong>드론 GPS</strong>예요. 드론 영상의 <strong>매 프레임(사진 한 장)</strong> 에는 그때 드론의 <strong>위도·경도(GPS)</strong> 가 기록돼 있어요. 이걸 이용해 프레임마다 두 가지를 계산해 <strong>짝지어</strong> 둬요.</p>
<figure class="fig">
<svg viewBox="0 0 720 330" role="img" aria-label="사진 1장마다 시간과 측점을 계산해 표로 모으고, 표로 양방향 변환">
<defs><marker id="ad" markerWidth="10" markerHeight="10" refX="7" refY="3.5" orient="auto"><path d="M0,0 L7,3.5 L0,7 Z" fill="#7c3aed"></path></marker></defs>
<!-- ① 왼쪽: 사진 1장 → 두 값 계산 -->
<text x="178" y="22" text-anchor="middle" font-size="12" font-weight="bold" fill="#334155">① 사진 1장마다 두 값을 계산</text>
<rect x="88" y="34" width="180" height="58" rx="8" fill="#f1f5f9" stroke="#475569"></rect>
<text x="178" y="57" text-anchor="middle" font-size="11.5" font-weight="bold" fill="#334155">🎞 프레임 5760번</text>
<text x="178" y="78" text-anchor="middle" font-size="10" fill="#475569">📍 드론 GPS 함께 기록됨</text>
<line x1="133" y1="92" x2="106" y2="118" stroke="#7c3aed" stroke-width="2" marker-end="url(#ad)"></line>
<line x1="223" y1="92" x2="250" y2="118" stroke="#7c3aed" stroke-width="2" marker-end="url(#ad)"></line>
<rect x="20" y="122" width="160" height="58" rx="8" fill="#eff6ff" stroke="#2563eb"></rect>
<text x="100" y="144" text-anchor="middle" font-size="11.5" font-weight="bold" fill="#1e3a8a">⏱ 시간 = 192초</text>
<text x="100" y="166" text-anchor="middle" font-size="9.5" fill="#1e40af">번호 5760 ÷ 30fps</text>
<rect x="196" y="122" width="160" height="58" rx="8" fill="#f0fdf4" stroke="#16a34a"></rect>
<text x="276" y="144" text-anchor="middle" font-size="11.5" font-weight="bold" fill="#166534">📐 측점 = 160k130</text>
<text x="276" y="166" text-anchor="middle" font-size="9.5" fill="#15803d">GPS를 선로에 &#39;&#39; 투영</text>
<!-- 가운데 화살표: 반복 -->
<line x1="368" y1="106" x2="418" y2="106" stroke="#7c3aed" stroke-width="3" marker-end="url(#ad)"></line>
<text x="393" y="92" text-anchor="middle" font-size="10" font-weight="bold" fill="#7c3aed">모든 사진에</text>
<text x="393" y="128" text-anchor="middle" font-size="10" font-weight="bold" fill="#7c3aed">반복</text>
<!-- ② 오른쪽: 시간↔측점 대응표 -->
<text x="560" y="22" text-anchor="middle" font-size="12" font-weight="bold" fill="#854d0e">&#39;시간↔측점 대응표&#39; 완성</text>
<rect x="440" y="34" width="240" height="26" fill="#fef3c7" stroke="#ca8a04"></rect>
<line x1="560" y1="34" x2="560" y2="190" stroke="#ca8a04" stroke-width="1"></line>
<text x="500" y="52" text-anchor="middle" font-size="11" font-weight="bold" fill="#1e3a8a">⏱ 시간</text>
<text x="620" y="52" text-anchor="middle" font-size="11" font-weight="bold" fill="#166534">📐 측점</text>
<rect x="440" y="60" width="240" height="26" fill="#ffffff" stroke="#ca8a04"></rect>
<text x="500" y="78" text-anchor="middle" font-size="10.5" fill="#334155">191초</text>
<text x="620" y="78" text-anchor="middle" font-size="10.5" fill="#334155">160k100</text>
<rect x="440" y="86" width="240" height="26" fill="#fef9e7" stroke="#ca8a04"></rect>
<text x="500" y="104" text-anchor="middle" font-size="10.5" font-weight="bold" fill="#1e3a8a">192초</text>
<text x="620" y="104" text-anchor="middle" font-size="10.5" font-weight="bold" fill="#166534">160k130</text>
<rect x="440" y="112" width="240" height="26" fill="#ffffff" stroke="#ca8a04"></rect>
<text x="500" y="130" text-anchor="middle" font-size="10.5" fill="#334155">193초</text>
<text x="620" y="130" text-anchor="middle" font-size="10.5" fill="#334155">160k160</text>
<rect x="440" y="138" width="240" height="26" fill="#ffffff" stroke="#ca8a04"></rect>
<text x="500" y="156" text-anchor="middle" font-size="10.5" fill="#94a3b8"></text>
<text x="620" y="156" text-anchor="middle" font-size="10.5" fill="#94a3b8"></text>
<rect x="440" y="164" width="240" height="26" fill="#ffffff" stroke="#ca8a04"></rect>
<text x="500" y="182" text-anchor="middle" font-size="10.5" fill="#334155">348초</text>
<text x="620" y="182" text-anchor="middle" font-size="10.5" fill="#334155">165k000</text>
<!-- ③ 아래: 표를 쓰는 두 방향 -->
<text x="360" y="232" text-anchor="middle" font-size="12" font-weight="bold" fill="#334155">③ 이 표 하나로, 어느 쪽으로든 찾기</text>
<rect x="110" y="246" width="500" height="30" rx="7" fill="#eff6ff" stroke="#2563eb"></rect>
<text x="360" y="266" text-anchor="middle" font-size="10.5" fill="#1e3a8a">재생 중: <tspan font-weight="bold">&quot;지금 192초… 여긴 어디?&quot;</tspan> → 표에서 찾아 → <tspan font-weight="bold" fill="#166534">측점 160k130 표시</tspan></text>
<rect x="110" y="284" width="500" height="30" rx="7" fill="#f0fdf4" stroke="#16a34a"></rect>
<text x="360" y="304" text-anchor="middle" font-size="10.5" fill="#166534">검색: <tspan font-weight="bold">&quot;160k130 가줘!&quot;</tspan> → 표에서 찾아 → <tspan font-weight="bold" fill="#1e3a8a">192초로 점프</tspan></text>
</svg>
<figcaption>사진 1장마다 (시간, 측점) 짝을 계산해(①) 표로 모아 두면(②), &quot;지금 어디?&quot;&quot;거기로 가줘!&quot;를 모두 이 표 하나로 해결한다(③). (코드: StationBar precompute)</figcaption>
</figure>
<h3 id="gps를-측점으로-바꾸는-법--선로에-수직으로-툭-">GPS를 측점으로 바꾸는 법 — &#39;선로에 수직으로 툭&#39; 📐</h3>
<p>드론은 선로 바로 위가 아니라 옆에서 비스듬히 날 때도 많아요. 그래서 드론 GPS 점을 <strong>선로(측점들을 이은 선)에 수직으로 툭 떨어뜨려</strong>, 그 발자국이 몇 측점인지 읽어요.</p>
<figure class="fig">
<svg viewBox="0 0 720 170" role="img" aria-label="드론 GPS를 선로에 수직 투영해 측점 읽기">
<line x1="60" y1="120" x2="660" y2="70" stroke="#8a7a5c" stroke-width="6"></line>
<text x="70" y="145" font-size="11" fill="#6b4f2a">선로 (측점 158k·159k·160k…를 이은 선)</text>
<circle cx="360" cy="32" r="7" fill="#2563eb"></circle><text x="360" y="24" text-anchor="middle" font-size="10" fill="#1e3a8a">드론 GPS</text>
<line x1="360" y1="32" x2="374" y2="94" stroke="#ef4444" stroke-width="2" stroke-dasharray="4 3"></line>
<circle cx="374" cy="94" r="5" fill="#ef4444"></circle>
<text x="410" y="98" font-size="11" fill="#b91c1c" font-weight="bold">수직으로 내린 발자국 = 160k130</text>
</svg>
<figcaption>드론이 선로 옆에 있어도, 수직으로 내린 발자국 위치로 &#39;측점값&#39;을 정확히 읽는다. (코드: chainage.ts projectToChain)</figcaption>
</figure>
<hr />
<h2 id="4-재생바의-가로축을-시간이-아니라-실제-이동-거리로-">4. 재생바의 가로축을 &#39;시간&#39;이 아니라 &#39;실제 이동 거리&#39;로 🚂</h2>
<p>여기가 특히 똑똑한 부분이에요. 드론이 <strong>멈춰서 맴돌면</strong> 시간은 계속 흐르지만 <strong>위치(측점)는 그대로</strong>예요. 이때 막대 커서가 계속 가면 이상하죠.</p>
<p>그래서 가로축을 <strong>&quot;실제로 이동한 거리&quot;</strong>(측점이 변한 양을 쌓은 값)로 만들었어요. → <strong>멈추면 커서도 멈추고, 움직일 때만 앞으로 가요.</strong></p>
<figure class="fig">
<svg viewBox="0 0 720 150" role="img" aria-label="이동거리 축 - 멈추면 커서 정지">
<text x="180" y="22" text-anchor="middle" font-size="12" font-weight="bold" fill="#475569">시간 축 (옛 방식)</text>
<rect x="40" y="34" width="280" height="20" rx="5" fill="#e5e7eb" stroke="#9ca3af"></rect><circle cx="240" cy="44" r="8" fill="#374151"></circle>
<text x="180" y="78" text-anchor="middle" font-size="10" fill="#b91c1c">드론이 멈춰 맴돌아도 커서가 계속 감 😵</text>
<text x="540" y="22" text-anchor="middle" font-size="12" font-weight="bold" fill="#166534">이동거리 축 (새 방식)</text>
<rect x="400" y="34" width="280" height="20" rx="5" fill="#fde9c8" stroke="#e0a94f"></rect><circle cx="560" cy="44" r="8" fill="#ea580c"></circle>
<text x="540" y="78" text-anchor="middle" font-size="10" fill="#166534">멈추면 커서도 멈춤, 움직일 때만 전진 😎</text>
<text x="360" y="120" text-anchor="middle" font-size="11" fill="#475569">→ 그래서 커서 위치만 봐도 &quot;드론이 실제로 얼마나 이동했는지&quot;를 알 수 있어요.</text>
</svg>
<figcaption>가로축 = 측점 변화량을 쌓은 &#39;이동거리&#39;. 전진이든 후진이든 움직이면 커서는 오른쪽으로 간다(방향은 색으로 구분). (코드: cumFracAtTime / timeAtFrac)</figcaption>
</figure>
<hr />
<h2 id="5-방향을-색으로--앞으로-가면-주황-되돌아오면-파랑-">5. 방향을 색으로 — 앞으로 가면 주황, 되돌아오면 파랑 🎨</h2>
<p>드론이 앞으로 가서 측점이 커지면 <strong>주황</strong>, 되돌아와서 측점이 작아지면 <strong>하늘색</strong> 으로 칠해요. 그래서 막대만 봐도 <strong>어디서 앞으로/뒤로 갔는지</strong> 알 수 있어요.</p>
<figure class="fig">
<svg viewBox="0 0 720 90" role="img" aria-label="진행 방향 색 구분">
<rect x="40" y="30" width="230" height="24" rx="5" fill="#f59e0b"></rect><text x="155" y="47" text-anchor="middle" font-size="11" fill="#7a3d00" font-weight="bold">전진(측점 ↑) 주황</text>
<rect x="270" y="30" width="150" height="24" rx="5" fill="#3fb6d4"></rect><text x="345" y="47" text-anchor="middle" font-size="11" fill="#06455a" font-weight="bold">후진(측점 ↓) 파랑</text>
<rect x="420" y="30" width="260" height="24" rx="5" fill="#f59e0b"></rect><text x="550" y="47" text-anchor="middle" font-size="11" fill="#7a3d00" font-weight="bold">다시 전진 주황</text>
<text x="360" y="78" text-anchor="middle" font-size="10" fill="#475569">색이 바뀌는 지점 = 드론이 방향을 바꾼 곳</text>
</svg>
</figure>
<hr />
<h2 id="6-구조물을-측점-위치에-딱-배치-">6. 구조물을 &#39;측점 위치&#39;에 딱 배치 🚉</h2>
<p>교량·터널·역을 각자의 <strong>측점값 위치</strong>에 표시해요. 각 시설물의 실제 측점(예: 회덕터널 160k)과, 드론이 <strong>그 측점을 실제로 지나는 시점</strong>을 맞춰서 막대에 점으로 찍어요. 그래서 &quot;이 구조물이 어디쯤인지&quot;를 막대만 봐도 알 수 있어요.</p>
<blockquote>
<p>드론이 같은 곳을 <strong>여러 번 지나가면</strong>(조차장에서 왔다갔다) 그 시설물 마커도 <strong>지날 때마다</strong> 찍혀요.</p>
</blockquote>
<hr />
<h2 id="7-측점으로-찾아가기-검색-">7. 측점으로 &#39;찾아가기&#39; (검색) 🔎</h2>
<p>측점값을 입력하면 → <strong>그 측점이 나오는 시간을 표에서 찾아 → 그 시간으로 이동</strong>해요. (3번에서 만든 &#39;시간↔︎측점 대응표&#39; 덕분!)</p>
<figure class="fig">
<svg viewBox="0 0 720 90" role="img" aria-label="측점 입력으로 이동">
<defs><marker id="as" markerWidth="10" markerHeight="10" refX="7" refY="3.5" orient="auto"><path d="M0,0 L7,3.5 L0,7 Z" fill="#7c3aed"></path></marker></defs>
<rect x="30" y="30" width="180" height="34" rx="6" fill="#f0fdf4" stroke="#16a34a"></rect><text x="120" y="52" text-anchor="middle" font-size="12" fill="#166534">&quot;160k130&quot; 입력</text>
<line x1="210" y1="47" x2="258" y2="47" stroke="#7c3aed" stroke-width="2" marker-end="url(#as)"></line>
<rect x="263" y="30" width="210" height="34" rx="6" fill="#fef3c7" stroke="#ca8a04"></rect><text x="368" y="52" text-anchor="middle" font-size="11" fill="#854d0e">표에서 시간 찾기 (192초)</text>
<line x1="473" y1="47" x2="521" y2="47" stroke="#7c3aed" stroke-width="2" marker-end="url(#as)"></line>
<rect x="526" y="30" width="164" height="34" rx="6" fill="#eff6ff" stroke="#2563eb"></rect><text x="608" y="52" text-anchor="middle" font-size="12" fill="#1e3a8a">그 시간으로 재생</text>
</svg>
<figcaption>여러 번 지난 측점이면 Enter 를 반복해 다음 통과 지점으로 순환 이동. 없는 측점이면 &#39;측점 없음&#39; 안내.</figcaption>
</figure>
<ul>
<li><strong>실제 측점(드론이 진짜 지난 곳)</strong> 으로만 이동해요. 이 영상에 없는 측점이면 엉뚱한 데로 안 가고 <strong>&quot;측점 없음&quot;</strong> 을 알려줘요.</li>
<li>드론이 <strong>끝(종점)까지 못 간</strong> 경우, 종점 측점은 막대 맨 끝에 <strong>&#39;미도착&#39;</strong> 으로 표시해요.</li>
</ul>
<hr />
<h2 id="8-전체-흐름-한눈에-컨베이어-벨트처럼-">8. 전체 흐름 한눈에 (컨베이어 벨트처럼) 🏭</h2>
<figure class="fig">
<svg viewBox="0 0 720 300" role="img" aria-label="전체 흐름">
<defs><marker id="a8" markerWidth="11" markerHeight="11" refX="7" refY="3.5" orient="auto"><path d="M0,0 L7,3.5 L0,7 Z" fill="#7c3aed"></path></marker></defs>
<rect x="210" y="12" width="300" height="40" rx="8" fill="#f1f5f9" stroke="#475569"></rect>
<text x="360" y="30" text-anchor="middle" font-size="12" font-weight="bold" fill="#334155">드론 영상 + GPS 기록 + 측점 데이터</text>
<text x="360" y="45" text-anchor="middle" font-size="10" fill="#475569">(폴더 선택으로 불러옴)</text>
<line x1="360" y1="52" x2="360" y2="70" stroke="#7c3aed" stroke-width="2" marker-end="url(#a8)"></line>
<g font-size="11">
<rect x="150" y="72" width="420" height="30" rx="6" fill="#f0fdf4" stroke="#16a34a"></rect>
<text x="360" y="92" text-anchor="middle" fill="#166534">① 프레임마다: GPS → 측점(선로에 수직투영), 프레임번호 → 시간</text>
<line x1="360" y1="102" x2="360" y2="118" stroke="#7c3aed" stroke-width="1.5" marker-end="url(#a8)"></line>
<rect x="150" y="120" width="420" height="30" rx="6" fill="#fef3c7" stroke="#ca8a04"></rect>
<text x="360" y="140" text-anchor="middle" fill="#854d0e">② 시간↔측점 대응표 + &#39;이동거리 축&#39; 만들기(멈추면 커서 정지)</text>
<line x1="360" y1="150" x2="360" y2="166" stroke="#7c3aed" stroke-width="1.5" marker-end="url(#a8)"></line>
<rect x="150" y="168" width="420" height="30" rx="6" fill="#eff6ff" stroke="#2563eb"></rect>
<text x="360" y="188" text-anchor="middle" fill="#1e3a8a">③ 막대 그리기: 방향색(주황/파랑) + 구조물(교량·터널·역) 배치</text>
<line x1="360" y1="198" x2="360" y2="214" stroke="#7c3aed" stroke-width="1.5" marker-end="url(#a8)"></line>
<rect x="150" y="216" width="420" height="30" rx="6" fill="#fdf2f8" stroke="#db2777"></rect>
<text x="360" y="236" text-anchor="middle" fill="#9d174d">④ 측점 입력 검색 → 표에서 시간 찾아 그 위치로 이동</text>
<line x1="360" y1="246" x2="360" y2="262" stroke="#7c3aed" stroke-width="1.5" marker-end="url(#a8)"></line>
<rect x="210" y="264" width="300" height="28" rx="6" fill="#f5f3ff" stroke="#7c3aed"></rect>
<text x="360" y="283" text-anchor="middle" fill="#5b21b6" font-weight="bold">🎯 측점으로 보는 주행영상 플레이어 완성</text>
</g>
</svg>
</figure>
<table>
<thead>
<tr class="header">
<th>단계</th>
<th>하는 일</th>
<th>파일·함수</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>① GPS→측점</td>
<td>드론 위치를 선로에 수직투영</td>
<td><a href="../client/src/utils/chainage.ts">chainage.ts</a> <code>projectToChain</code></td>
</tr>
<tr class="even">
<td>① 프레임→시간</td>
<td>프레임번호 ÷ fps</td>
<td>StationBar <code>videoFps</code></td>
</tr>
<tr class="odd">
<td>② 시간↔︎측점 대응표</td>
<td>프레임별 사전계산</td>
<td>StationBar precompute</td>
</tr>
<tr class="even">
<td>② 이동거리 축</td>
<td>측점 변화량 누적</td>
<td><code>cumFracAtTime</code> / <code>timeAtFrac</code></td>
</tr>
<tr class="odd">
<td>③ 방향색·구조물</td>
<td>측점 증감/측점값 기준</td>
<td><code>barSegments</code> / <code>structureMarks</code></td>
</tr>
<tr class="even">
<td>④ 측점 검색</td>
<td>측점→시간 역변환</td>
<td><code>handleJumpToMileage</code></td>
</tr>
</tbody>
</table>
<hr />
<h2 id="9-한-문장-결론-">9. 한 문장 결론 🎁</h2>
<blockquote>
<p><strong>&quot;드론 GPS로 매 장면의 측점을 계산해 &#39;시간↔︎측점 대응표&#39;를 만들어 두었기 때문에, 시간이 아니라 측점(선로 위치)으로 영상을 찾아보고 이동할 수 있게 된 것&quot;</strong> 이에요.</p>
</blockquote>
<p>이 덕분에 GhiVideo는 단순 영상이 아니라 <strong>&quot;지금 선로 어디를 보고 있는지 아는&quot; 주행영상 플레이어</strong>가 됩니다. 🚁📏</p>
</body>
</html>