기존 GhiVideo 저장소 HEAD의 트래킹 소스 362개 파일을 복제. (node_modules·storage·빌드 산출물·대용량 미디어는 .gitignore 규칙대로 제외) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
344 lines
23 KiB
HTML
344 lines
23 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>발표_GhiVideo_좌표투영과_측점기반재생</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">발표_GhiVideo_좌표투영과_측점기반재생</h1>
|
|
</header>
|
|
<h1 id="ghivideo-핵심-기술-두-가지-발표-자료">GhiVideo 핵심 기술 두 가지 (발표 자료)</h1>
|
|
<blockquote>
|
|
<p>드론 주행영상 플레이어 <strong>GhiVideo</strong> 의 두 가지 핵심 기술을 <strong>초등학생도 이해할 수 있게</strong> 그림으로 설명합니다. <strong>① 드론 좌표를 영상에 딱 맞추기(좌표 투영)</strong> · <strong>② 시간이 아니라 '측점'으로 움직이는 재생 바</strong></p>
|
|
</blockquote>
|
|
<style>
|
|
figure.fig{margin:20px 0;text-align:center;page-break-inside:avoid;break-inside:avoid;}
|
|
figure.fig svg{width:100%;max-width:700px;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;}
|
|
</style>
|
|
|
|
<hr />
|
|
<h2 id="0-한눈에--ghivideo는-뭐가-특별한가요">0. 한눈에 — GhiVideo는 뭐가 특별한가요?</h2>
|
|
<p>보통 영상 플레이어는 "그냥 영상 재생"이 끝이에요. GhiVideo는 여기에 <strong>두 가지 마법</strong>을 더했어요.</p>
|
|
<figure class="fig">
|
|
<svg viewBox="0 0 700 210" role="img" aria-label="GhiVideo 두 가지 핵심">
|
|
<rect x="30" y="30" width="300" height="150" rx="12" fill="#eff6ff" stroke="#2563eb"></rect>
|
|
<text x="180" y="58" text-anchor="middle" font-size="14" font-weight="bold" fill="#1e3a8a">① 좌표 투영</text>
|
|
<text x="180" y="86" text-anchor="middle" font-size="11" fill="#1e40af">지도 속 터널·다리 위치를</text>
|
|
<text x="180" y="106" text-anchor="middle" font-size="11" fill="#1e40af">영상 화면의 정확한 자리에</text>
|
|
<text x="180" y="126" text-anchor="middle" font-size="11" fill="#1e40af">이름표로 딱 붙이기</text>
|
|
<text x="180" y="158" text-anchor="middle" font-size="22">🚇🏷️</text>
|
|
<rect x="370" y="30" width="300" height="150" rx="12" fill="#f0fdf4" stroke="#16a34a"></rect>
|
|
<text x="520" y="58" text-anchor="middle" font-size="14" font-weight="bold" fill="#166534">② 측점 기반 재생 바</text>
|
|
<text x="520" y="86" text-anchor="middle" font-size="11" fill="#15803d">시간(0:00) 대신</text>
|
|
<text x="520" y="106" text-anchor="middle" font-size="11" fill="#15803d">철도 '측점(160k130)'으로</text>
|
|
<text x="520" y="126" text-anchor="middle" font-size="11" fill="#15803d">찾아가는 재생 바</text>
|
|
<text x="520" y="158" text-anchor="middle" font-size="22">📏▶️</text>
|
|
</svg>
|
|
</figure>
|
|
|
|
<hr />
|
|
<h1 id="part-1-드론-좌표를-영상에-딱-맞추기-">Part 1. 드론 좌표를 영상에 '딱' 맞추기 🎯</h1>
|
|
<h2 id="1-1-풀고-싶은-문제">1-1. 풀고 싶은 문제</h2>
|
|
<p>드론이 <strong>앞으로 날며</strong> 앞쪽 땅을 비스듬히 찍어요. 이 영상 위에 <strong>"여기가 회덕터널!"</strong> 이름표를 붙이고 싶어요. 드론이 계속 움직이니까 이름표도 <strong>졸졸 따라다녀야</strong> 해요.</p>
|
|
<figure class="fig">
|
|
<svg viewBox="0 0 700 220" role="img" aria-label="드론이 앞을 보며 촬영">
|
|
<defs>
|
|
<marker id="mv" markerWidth="12" markerHeight="12" refX="8" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8 Z" fill="#2563eb"></path></marker>
|
|
<marker id="mc" markerWidth="12" markerHeight="12" refX="8" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8 Z" fill="#ea580c"></path></marker>
|
|
</defs>
|
|
<rect x="0" y="0" width="700" height="150" fill="#eef6ff"></rect><rect x="0" y="150" width="700" height="70" fill="#eaf7ea"></rect>
|
|
<line x1="0" y1="150" x2="700" y2="150" stroke="#bcd8bc" stroke-width="2"></line>
|
|
<g transform="translate(110,50)"><rect x="-24" y="-7" width="48" height="14" rx="4" fill="#334155"></rect><circle cx="-24" cy="0" r="8" fill="none" stroke="#334155" stroke-width="3"></circle><circle cx="24" cy="0" r="8" fill="none" stroke="#334155" stroke-width="3"></circle><text x="0" y="-14" text-anchor="middle" font-size="12" font-weight="bold" fill="#334155">드론</text></g>
|
|
<line x1="140" y1="50" x2="280" y2="50" stroke="#2563eb" stroke-width="3" stroke-dasharray="7 5" marker-end="url(#mv)"></line><text x="220" y="40" text-anchor="middle" font-size="12" fill="#2563eb" font-weight="bold">이동 방향 ▶</text>
|
|
<line x1="118" y1="58" x2="470" y2="148" stroke="#ea580c" stroke-width="3" marker-end="url(#mc)"></line><text x="300" y="105" text-anchor="middle" font-size="12" fill="#ea580c" font-weight="bold">카메라 시선(앞·아래로 비스듬히)</text>
|
|
<text x="485" y="145" font-size="20">🚇</text><text x="520" y="175" text-anchor="middle" font-size="12" font-weight="bold" fill="#166534">회덕터널 (앞쪽!)</text>
|
|
<line x1="60" y1="200" x2="660" y2="200" stroke="#8a7a5c" stroke-width="5"></line>
|
|
</svg>
|
|
<figcaption>드론은 앞으로 날며 앞쪽 땅을 비스듬히 본다. 터널은 드론 바로 밑이 아니라 '앞쪽'에 찍힌다.</figcaption>
|
|
</figure>
|
|
|
|
<h2 id="1-2-핵심-아이디어--창문에-스티커-붙이기-">1-2. 핵심 아이디어 — '창문에 스티커 붙이기' 🪟</h2>
|
|
<p>카메라(눈)에서 터널로 <strong>선을 긋고</strong>, 그 선이 <strong>화면(창문)을 뚫는 점</strong>에 이름표를 붙여요.</p>
|
|
<figure class="fig">
|
|
<svg viewBox="0 0 700 210" role="img" aria-label="창문 스티커 원리">
|
|
<defs><marker id="me" markerWidth="12" markerHeight="12" refX="8" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8 Z" fill="#0f766e"></path></marker></defs>
|
|
<text x="70" y="115" text-anchor="middle" font-size="30">👁️</text><text x="70" y="150" text-anchor="middle" font-size="12" fill="#0f766e" font-weight="bold">카메라(눈)</text>
|
|
<text x="620" y="100" text-anchor="middle" font-size="36">🌳</text><text x="620" y="140" text-anchor="middle" font-size="12" fill="#166534" font-weight="bold">터널(나무)</text>
|
|
<line x1="95" y1="108" x2="600" y2="88" stroke="#0f766e" stroke-width="2.5" marker-end="url(#me)"></line>
|
|
<rect x="330" y="35" width="80" height="150" rx="6" fill="#e0f2fe" stroke="#0284c7" stroke-width="2" opacity="0.8"></rect><text x="370" y="200" text-anchor="middle" font-size="12" fill="#0369a1" font-weight="bold">창문 = 영상 화면</text>
|
|
<circle cx="370" cy="97" r="8" fill="#ef4444"></circle><text x="370" y="78" text-anchor="middle" font-size="12" fill="#b91c1c" font-weight="bold">스티커!(이름표 자리)</text>
|
|
</svg>
|
|
</figure>
|
|
|
|
<h2 id="1-3-실제로는-3단계로-계산해요">1-3. 실제로는 3단계로 계산해요</h2>
|
|
<figure class="fig">
|
|
<svg viewBox="0 0 700 140" role="img" aria-label="투영 3단계">
|
|
<defs><marker id="a3" 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="20" y="45" width="190" height="50" rx="8" fill="#ecfdf5" stroke="#16a34a"></rect><text x="115" y="68" text-anchor="middle" font-size="12" font-weight="bold" fill="#166534">① 미터로 바꿔</text><text x="115" y="85" text-anchor="middle" font-size="10" fill="#166534">드론 기준 위치 재기</text>
|
|
<line x1="210" y1="70" x2="245" y2="70" stroke="#7c3aed" stroke-width="2" marker-end="url(#a3)"></line>
|
|
<rect x="250" y="45" width="190" height="50" rx="8" fill="#eff6ff" stroke="#2563eb"></rect><text x="345" y="68" text-anchor="middle" font-size="12" font-weight="bold" fill="#1e3a8a">② 카메라 기울기만큼</text><text x="345" y="85" text-anchor="middle" font-size="10" fill="#1e3a8a">방향 돌리기(회전)</text>
|
|
<line x1="440" y1="70" x2="475" y2="70" stroke="#7c3aed" stroke-width="2" marker-end="url(#a3)"></line>
|
|
<rect x="480" y="45" width="200" height="50" rx="8" fill="#fdf2f8" stroke="#db2777"></rect><text x="580" y="68" text-anchor="middle" font-size="12" font-weight="bold" fill="#9d174d">③ 화면 점 찾기</text><text x="580" y="85" text-anchor="middle" font-size="10" fill="#9d174d">(창문 스티커 · 핀홀)</text>
|
|
</svg>
|
|
<figcaption>결과: "터널은 화면 가로 60%, 세로 70% 지점" → 거기에 이름표를 딱!</figcaption>
|
|
</figure>
|
|
|
|
<table>
|
|
<thead>
|
|
<tr class="header">
|
|
<th>단계</th>
|
|
<th>진짜 기술</th>
|
|
<th>쉽게 말하면</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr class="odd">
|
|
<td>① 미터로 바꾸기</td>
|
|
<td>proj4 · EPSG:5186(한국 TM)</td>
|
|
<td>둥근 지구를 평평한 미터 지도로 펴기</td>
|
|
</tr>
|
|
<tr class="even">
|
|
<td>② 방향 돌리기</td>
|
|
<td>회전행렬(yaw·pitch·roll)</td>
|
|
<td>카메라 기울기를 숫자표로 한 번에 적용</td>
|
|
</tr>
|
|
<tr class="odd">
|
|
<td>③ 화면 점 찾기</td>
|
|
<td>핀홀 카메라 모델·초점거리</td>
|
|
<td>바늘구멍 사진기 원리로 점 찍기</td>
|
|
</tr>
|
|
<tr class="even">
|
|
<td>(+) 부드럽게</td>
|
|
<td>보간 · EMA 평활</td>
|
|
<td>30장 사이 채우고 드론 떨림 없애기</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<blockquote>
|
|
<p>💡 원래 파이썬 실험 프로그램(<code>advanced_tuner_v2.py</code>)의 계산을 <strong>웹 브라우저(TypeScript)로 이식</strong> → 설치 없이 실행.</p>
|
|
</blockquote>
|
|
<hr />
|
|
<h1 id="part-2-시간이-아니라-측점으로-움직이는-재생-바-">Part 2. 시간이 아니라 '측점'으로 움직이는 재생 바 📏</h1>
|
|
<h2 id="2-1-무엇이-다른가요">2-1. 무엇이 다른가요?</h2>
|
|
<p>보통 플레이어의 아래 막대는 <strong>시간(0:00 ~ 끝)</strong> 기준이에요. 그런데 철도 점검은 <strong>"몇 분"이 아니라 "몇 측점"</strong> 으로 말해요. 그래서 GhiVideo는 막대를 <strong>측점(측점값, 예: 160k130)</strong> 기준으로 바꿨어요.</p>
|
|
<figure class="fig">
|
|
<svg viewBox="0 0 700 230" role="img" aria-label="시간 축 재생바 vs 측점 축 재생바">
|
|
<text x="350" y="24" text-anchor="middle" font-size="13" font-weight="bold" fill="#475569">보통 플레이어 (시간 축)</text>
|
|
<rect x="40" y="38" width="620" height="26" rx="6" fill="#e5e7eb" stroke="#9ca3af"></rect>
|
|
<rect x="40" y="38" width="260" height="26" rx="6" fill="#9ca3af"></rect>
|
|
<circle cx="300" cy="51" r="10" fill="#374151"></circle>
|
|
<text x="45" y="82" font-size="11" fill="#374151">0:00</text><text x="330" y="82" font-size="11" fill="#374151">03:12</text><text x="632" y="82" font-size="11" fill="#374151">09:28</text>
|
|
<text x="350" y="104" text-anchor="middle" font-size="11" fill="#6b7280">"3분 12초 지점" — 여기가 어디 선로인지 모름 😵</text>
|
|
<text x="350" y="150" text-anchor="middle" font-size="13" font-weight="bold" fill="#166534">GhiVideo (측점 축)</text>
|
|
<rect x="40" y="164" width="620" height="26" rx="6" fill="#fde9c8" stroke="#e0a94f"></rect>
|
|
<rect x="40" y="164" width="300" height="26" rx="6" fill="#f59e0b"></rect>
|
|
<circle cx="340" cy="177" r="11" fill="#ea580c"></circle><text x="340" y="181" text-anchor="middle" font-size="9" fill="#fff" font-weight="bold">160k</text>
|
|
<line x1="200" y1="160" x2="200" y2="194" stroke="#0284c7" stroke-width="2"></line><text x="200" y="210" text-anchor="middle" font-size="9" fill="#0369a1">회덕터널</text>
|
|
<line x1="470" y1="160" x2="470" y2="194" stroke="#0284c7" stroke-width="2"></line><text x="470" y="210" text-anchor="middle" font-size="9" fill="#0369a1">회덕천교</text>
|
|
<text x="45" y="228" font-size="11" fill="#166534">대전조차장</text><text x="600" y="228" font-size="11" fill="#166534">신탄진</text>
|
|
</svg>
|
|
<figcaption>시간 축은 "몇 분"만 안다. 측점 축은 "지금 어느 선로 위치(측점)"와 "무슨 구조물 근처"인지 바로 보인다.</figcaption>
|
|
</figure>
|
|
|
|
<h2 id="2-2-측점이-뭐예요-">2-2. 측점이 뭐예요? 📍</h2>
|
|
<p>철도에서 <strong>출발점부터 몇 미터 왔는지</strong>를 나타내는 '거리 이정표'예요. <code>160k130</code> = 출발점에서 <strong>160km + 130m</strong> 지점. (도로의 'km 표지판'과 똑같아요!)</p>
|
|
<h2 id="2-3-어떻게-만들었나--시간측점-대응표-️">2-3. 어떻게 만들었나 — "시간↔︎측점 대응표" 🗺️</h2>
|
|
<p>비밀은 <strong>드론 GPS</strong>예요. 영상의 <strong>매 프레임(사진 한 장)</strong> 마다 두 가지를 계산해 <strong>짝지어</strong> 둬요:</p>
|
|
<figure class="fig">
|
|
<svg viewBox="0 0 700 250" role="img" aria-label="프레임마다 시간과 측점을 짝짓기">
|
|
<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>
|
|
<rect x="250" y="15" width="200" height="40" rx="8" fill="#f1f5f9" stroke="#475569"></rect>
|
|
<text x="350" y="33" text-anchor="middle" font-size="12" font-weight="bold" fill="#334155">영상 프레임 (사진 1장)</text>
|
|
<text x="350" y="49" text-anchor="middle" font-size="10" fill="#475569">번호 + 드론 GPS 기록</text>
|
|
<line x1="300" y1="55" x2="200" y2="80" stroke="#7c3aed" stroke-width="2" marker-end="url(#ad)"></line>
|
|
<line x1="400" y1="55" x2="500" y2="80" stroke="#7c3aed" stroke-width="2" marker-end="url(#ad)"></line>
|
|
<rect x="70" y="82" width="260" height="52" rx="8" fill="#eff6ff" stroke="#2563eb"></rect>
|
|
<text x="200" y="103" text-anchor="middle" font-size="12" font-weight="bold" fill="#1e3a8a">프레임번호 ÷ fps = 시간</text>
|
|
<text x="200" y="122" text-anchor="middle" font-size="10" fill="#1e40af">예: 5760번 ÷ 30 = 192초</text>
|
|
<rect x="370" y="82" width="270" height="52" rx="8" fill="#f0fdf4" stroke="#16a34a"></rect>
|
|
<text x="505" y="103" text-anchor="middle" font-size="12" font-weight="bold" fill="#166534">GPS → 측점 (선로에 투영)</text>
|
|
<text x="505" y="122" text-anchor="middle" font-size="10" fill="#15803d">예: 위/경도 → 160k130</text>
|
|
<line x1="200" y1="134" x2="330" y2="170" stroke="#7c3aed" stroke-width="2" marker-end="url(#ad)"></line>
|
|
<line x1="505" y1="134" x2="370" y2="170" stroke="#7c3aed" stroke-width="2" marker-end="url(#ad)"></line>
|
|
<rect x="210" y="172" width="280" height="46" rx="8" fill="#fef3c7" stroke="#ca8a04"></rect>
|
|
<text x="350" y="193" text-anchor="middle" font-size="12" font-weight="bold" fill="#854d0e">시간 ↔ 측점 대응표 완성!</text>
|
|
<text x="350" y="210" text-anchor="middle" font-size="10" fill="#a16207">"192초 = 160k130" 처럼 서로 변환 가능</text>
|
|
</svg>
|
|
<figcaption>프레임마다 (시간)과 (측점)을 계산해 짝지으면, 시간↔측점을 자유롭게 오갈 수 있다 — 재생 기준을 '시간'에서 '스테이션(측점)'으로 전환하기 위한 색인 구조.</figcaption>
|
|
</figure>
|
|
|
|
<p><strong>GPS를 측점으로 바꾸는 법(②):</strong> 드론 GPS 점을 <strong>선로(측점들을 이은 선)에 수직으로 툭 떨어뜨려</strong>, 그 발자국이 몇 측점인지 읽어요. (코드: <code>projectToChain</code> / <code>projectChainage</code>)</p>
|
|
<figure class="fig">
|
|
<svg viewBox="0 0 700 160" role="img" aria-label="드론 GPS를 선로에 수직 투영">
|
|
<line x1="60" y1="110" x2="640" y2="70" stroke="#8a7a5c" stroke-width="6"></line>
|
|
<text x="70" y="135" font-size="11" fill="#6b4f2a">선로(측점들을 이은 선)</text>
|
|
<circle cx="360" cy="30" r="7" fill="#2563eb"></circle><text x="360" y="22" text-anchor="middle" font-size="10" fill="#1e3a8a">드론 GPS</text>
|
|
<line x1="360" y1="30" x2="373" y2="93" stroke="#ef4444" stroke-width="2" stroke-dasharray="4 3"></line>
|
|
<circle cx="373" cy="93" r="5" fill="#ef4444"></circle><text x="410" y="96" font-size="11" fill="#b91c1c" font-weight="bold">발자국 = 160k130</text>
|
|
</svg>
|
|
<figcaption>드론이 선로 바로 위가 아니어도, 수직으로 내린 발자국 위치로 '측점값'을 정확히 읽는다.</figcaption>
|
|
</figure>
|
|
|
|
<h2 id="2-4-똑똑한-점-3가지-">2-4. 똑똑한 점 3가지 ✨</h2>
|
|
<h3 id="①-막대의-가로축--시간이-아니라-실제-이동-거리">① 막대의 가로축 = '시간'이 아니라 '실제 이동 거리'</h3>
|
|
<p>드론이 <strong>멈춰서 맴돌면 시간은 흐르지만 위치는 그대로</strong>예요. 이때 막대 커서가 계속 가면 이상하죠. 그래서 가로축을 <strong>실제 이동한 거리(측점 변화량 누적)</strong> 로 만들었어요. → <strong>멈추면 커서도 멈추고, 움직이면 전진.</strong></p>
|
|
<figure class="fig">
|
|
<svg viewBox="0 0 700 150" role="img" aria-label="이동거리 축 - 멈추면 커서 정지">
|
|
<text x="175" y="22" text-anchor="middle" font-size="12" font-weight="bold" fill="#475569">시간 축(옛방식)</text>
|
|
<rect x="40" y="34" width="270" height="20" rx="5" fill="#e5e7eb" stroke="#9ca3af"></rect><circle cx="230" cy="44" r="8" fill="#374151"></circle>
|
|
<text x="175" y="78" text-anchor="middle" font-size="10" fill="#b91c1c">드론이 멈춰도 커서가 계속 감 😵</text>
|
|
<text x="525" y="22" text-anchor="middle" font-size="12" font-weight="bold" fill="#166534">이동거리 축(새방식)</text>
|
|
<rect x="390" y="34" width="270" height="20" rx="5" fill="#fde9c8" stroke="#e0a94f"></rect><circle cx="560" cy="44" r="8" fill="#ea580c"></circle>
|
|
<text x="525" y="78" text-anchor="middle" font-size="10" fill="#166534">멈추면 커서도 멈춤, 움직일 때만 전진 😎</text>
|
|
</svg>
|
|
</figure>
|
|
|
|
<h3 id="②-방향을-색으로--측점이-늘면-주황-줄면-하늘색">② 방향을 색으로 — 측점이 늘면 주황, 줄면 하늘색</h3>
|
|
<p>드론이 앞으로 가면(측점 증가) <strong>주황</strong>, 되돌아오면(측점 감소) <strong>하늘색</strong> 으로 칠해 <strong>진행 방향</strong>을 한눈에 보여줘요.</p>
|
|
<h3 id="③-구조물을-제자리에--교량터널역은-측점값에-배치">③ 구조물을 제자리에 — 교량·터널·역은 '측점값'에 배치</h3>
|
|
<p>교량·터널·역사를 각자의 <strong>측점값 위치</strong>에 표시해서, "이 구조물이 어디쯤인지" 막대만 봐도 알 수 있어요.</p>
|
|
<h2 id="2-5-측점으로-찾아가기seek">2-5. 측점으로 '찾아가기'(Seek)</h2>
|
|
<p>측점값을 입력하거나 막대를 누르면 → <strong>그 측점이 나오는 시간을 지도에서 찾아 → 그 시간으로 이동</strong>해요. (시간↔︎측점 대응표가 있으니 가능한 일!)</p>
|
|
<figure class="fig">
|
|
<svg viewBox="0 0 700 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">"160k130" 입력</text>
|
|
<line x1="210" y1="47" x2="255" y2="47" stroke="#7c3aed" stroke-width="2" marker-end="url(#as)"></line>
|
|
<rect x="260" y="30" width="200" height="34" rx="6" fill="#fef3c7" stroke="#ca8a04"></rect><text x="360" y="52" text-anchor="middle" font-size="11" fill="#854d0e">지도에서 시간 찾기(192초)</text>
|
|
<line x1="460" y1="47" x2="505" y2="47" stroke="#7c3aed" stroke-width="2" marker-end="url(#as)"></line>
|
|
<rect x="510" y="30" width="160" height="34" rx="6" fill="#eff6ff" stroke="#2563eb"></rect><text x="590" y="52" text-anchor="middle" font-size="12" fill="#1e3a8a">그 시간으로 재생</text>
|
|
</svg>
|
|
</figure>
|
|
|
|
<h2 id="2-6-핵심-기술--파일">2-6. 핵심 기술 / 파일</h2>
|
|
<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>VideoPlayer.tsx <code>effectiveFps</code></td>
|
|
</tr>
|
|
<tr class="odd">
|
|
<td>시간↔︎측점 대응표</td>
|
|
<td>프레임별 precompute</td>
|
|
<td><a href="../client/src/stationbar/StationBar.tsx">StationBar.tsx</a></td>
|
|
</tr>
|
|
<tr class="even">
|
|
<td>이동거리 축</td>
|
|
<td>측점 변화량 누적(정규화)</td>
|
|
<td>StationBar.tsx <code>frac</code></td>
|
|
</tr>
|
|
<tr class="odd">
|
|
<td>방향 색·구조물 배치</td>
|
|
<td>측점값 기준 렌더</td>
|
|
<td>stationbar/components/*</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<hr />
|
|
<h2 id="마무리--한-문장씩-">마무리 — 한 문장씩 🎁</h2>
|
|
<ul>
|
|
<li><strong>Part 1 (좌표 투영):</strong> "드론이 <strong>어디서·어느 쪽</strong>을 보는지 알기 때문에, 지도 속 터널이 <strong>영상 화면 어디</strong>에 보일지 계산해 이름표를 딱 붙인다."</li>
|
|
<li><strong>Part 2 (측점 기반):</strong> "드론 <strong>GPS로 매 프레임의 측점</strong>을 계산해 <strong>시간↔︎측점 대응표</strong>를 만들어, <strong>시간이 아니라 측점으로</strong> 영상을 탐색한다."</li>
|
|
</ul>
|
|
<blockquote>
|
|
<p>두 기술 모두 <strong>드론의 GPS·자세 데이터</strong>를 똑똑하게 활용한 결과예요. 그래서 GhiVideo는 단순 영상이 아니라 <strong>"선로 위 어디를 보고 있는지 아는" 주행영상 플레이어</strong>가 됩니다. 🚁📏</p>
|
|
</blockquote>
|
|
</body>
|
|
</html>
|