feat: 지도 나침반(OSM/위성) 추가 + 측점 검색·마커 정확도 개선 + POI/영상 수정

- 지도 기반 나침반(노스업, 현재위치, 시야 역삼각형): 호버 확대·휠 줌·클릭 위성전환, OSM/Esri 타일 서버 프록시(/api/tile)
- 스테이션 검색: 실제 측점(chain) 기준 이동, 없으면 '측점 없음' 안내
- 역 마커: 직교 투영 측점 일치 시에만 표시, 미도착 종점은 추가 방식
- POI 팝업 겹침/재등장·라벨 정합 수정
- 영상 fps 데이터 기반 자동 산출
- 기술/발표/쉬운설명 문서 추가

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-02 18:03:30 +09:00
co-authored by Claude Opus 4.8
parent e9052143d7
commit e0ff5dd6d0
58 changed files with 9635 additions and 378 deletions
@@ -0,0 +1,396 @@
# 드론이 본 것을 지도랑 '딱' 맞추는 마법 (발표용 쉬운 설명서)
> 이 글은 **초등학생도 이해할 수 있게** 그림과 이야기로 쓴 발표 자료예요.
> 주제: **"드론이 찍은 영상 위에, 지도 속 터널·다리 이름표를 어떻게 정확한 자리에 붙일까?"**
> (진짜 수학 공식은 [geoProjection.ts](../client/src/utils/geoProjection.ts) 참고)
<style>
figure.fig{margin:22px 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;}
.big{font-size:1.05em;line-height:1.7;}
</style>
---
## 0. 한 문장으로 말하면
**"드론이 지금 어디서, 어느 쪽을 보고 있는지"** 를 알기 때문에,
**"지도 속 터널이 영상 화면의 어느 점에 보일지"** 를 계산해서 이름표를 딱 붙이는 기술이에요.
> 🎯 **비유**: 내가 서 있는 자리와 바라보는 방향을 알면,
> "저 멀리 나무가 내 눈앞 어디쯤 보일지" 알 수 있죠? 그걸 컴퓨터가 대신 해 주는 거예요.
---
## 1. 우리가 풀고 싶은 문제 — 드론은 '앞을 보며' 날아가요
드론은 가만히 아래를 내려다보는 게 아니라, **앞으로 날아가면서 자기 앞쪽 땅을 비스듬히** 봐요.
(자동차 운전할 때 바로 앞 범퍼가 아니라, 저~ 앞 도로를 보는 것과 똑같아요! 🚗)
<figure class="fig">
<svg viewBox="0 0 700 340" 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"/></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"/></marker>
</defs>
<!-- 하늘/땅 -->
<rect x="0" y="0" width="700" height="230" fill="#eef6ff"/>
<rect x="0" y="230" width="700" height="110" fill="#eaf7ea"/>
<line x1="0" y1="230" x2="700" y2="230" stroke="#bcd8bc" stroke-width="2"/>
<!-- 드론 -->
<g transform="translate(120,70)">
<rect x="-26" y="-8" width="52" height="16" rx="4" fill="#334155"/>
<circle cx="-26" cy="0" r="9" fill="none" stroke="#334155" stroke-width="3"/>
<circle cx="26" cy="0" r="9" fill="none" stroke="#334155" stroke-width="3"/>
<text x="0" y="-16" text-anchor="middle" font-size="13" font-weight="bold" fill="#334155">드론</text>
</g>
<!-- 이동 방향 -->
<line x1="150" y1="70" x2="300" y2="70" stroke="#2563eb" stroke-width="3" stroke-dasharray="7 5" marker-end="url(#mv)"/>
<text x="235" y="60" text-anchor="middle" font-size="13" fill="#2563eb" font-weight="bold">이동 방향 ▶</text>
<!-- 카메라 시선 (앞·아래로 비스듬히) -->
<line x1="128" y1="80" x2="470" y2="228" stroke="#ea580c" stroke-width="3" marker-end="url(#mc)"/>
<text x="300" y="150" text-anchor="middle" font-size="13" fill="#ea580c" font-weight="bold">카메라 시선 (앞·아래로 비스듬히)</text>
<!-- 드론 바로 아래 -->
<line x1="120" y1="80" x2="120" y2="230" stroke="#94a3b8" stroke-width="2" stroke-dasharray="4 4"/>
<text x="120" y="250" text-anchor="middle" font-size="11" fill="#64748b">드론 바로 밑</text>
<!-- 터널 -->
<text x="480" y="222" text-anchor="middle" font-size="22">🚇</text>
<text x="520" y="255" text-anchor="middle" font-size="13" font-weight="bold" fill="#166534">회덕터널 (드론보다 앞!)</text>
<!-- 기찻길 -->
<line x1="60" y1="300" x2="660" y2="300" stroke="#8a7a5c" stroke-width="6"/>
<line x1="60" y1="290" x2="660" y2="290" stroke="#8a7a5c" stroke-width="2"/>
<line x1="60" y1="310" x2="660" y2="310" stroke="#8a7a5c" stroke-width="2"/>
</svg>
<figcaption>드론은 앞으로 날며(파란 화살표), 자기보다 <b>앞쪽</b> 땅을 비스듬히 본다(주황 시선). 그래서 터널은 드론 바로 밑이 아니라 <b>앞쪽</b>에 찍힌다.</figcaption>
</figure>
**어려운 점:** 드론은 계속 날아가고 바람에 흔들려요. 그래서 이름표도 터널을 **졸졸 따라다녀야** 해요.
→ "이 터널이 지금 화면의 **어느 점**에 보이는지"를 매 순간(1초에 60번!) 계산해야 해요.
---
## 2. 우리가 가진 '단서' 4가지 🔍
탐정처럼, 우리는 이미 몇 가지를 알고 있어요. 이 4개만 있으면 계산이 돼요!
<figure class="fig">
<svg viewBox="0 0 700 200" role="img" aria-label="계산에 필요한 단서 4가지">
<rect x="20" y="30" width="150" height="140" rx="10" fill="#ecfdf5" stroke="#16a34a"/>
<text x="95" y="60" text-anchor="middle" font-size="26">📍</text>
<text x="95" y="95" text-anchor="middle" font-size="13" font-weight="bold" fill="#166534">① 드론 위치</text>
<text x="95" y="118" text-anchor="middle" font-size="11" fill="#166534">하늘의 '주소'</text>
<text x="95" y="136" text-anchor="middle" font-size="11" fill="#166534">(GPS 위도·경도·높이)</text>
<rect x="190" y="30" width="150" height="140" rx="10" fill="#eff6ff" stroke="#2563eb"/>
<text x="265" y="60" text-anchor="middle" font-size="26">🧭</text>
<text x="265" y="95" text-anchor="middle" font-size="13" font-weight="bold" fill="#1e3a8a">② 보는 방향</text>
<text x="265" y="118" text-anchor="middle" font-size="11" fill="#1e3a8a">어디를·얼마나</text>
<text x="265" y="136" text-anchor="middle" font-size="11" fill="#1e3a8a">기울여 보나</text>
<rect x="360" y="30" width="150" height="140" rx="10" fill="#fefce8" stroke="#ca8a04"/>
<text x="435" y="60" text-anchor="middle" font-size="26">🚇</text>
<text x="435" y="95" text-anchor="middle" font-size="13" font-weight="bold" fill="#854d0e">③ 터널 위치</text>
<text x="435" y="118" text-anchor="middle" font-size="11" fill="#854d0e">땅의 '주소'</text>
<text x="435" y="136" text-anchor="middle" font-size="11" fill="#854d0e">(지도 GPS)</text>
<rect x="530" y="30" width="150" height="140" rx="10" fill="#fdf2f8" stroke="#db2777"/>
<text x="605" y="60" text-anchor="middle" font-size="26">🔎</text>
<text x="605" y="95" text-anchor="middle" font-size="13" font-weight="bold" fill="#9d174d">④ 렌즈 정보</text>
<text x="605" y="118" text-anchor="middle" font-size="11" fill="#9d174d">얼마나 당겨</text>
<text x="605" y="136" text-anchor="middle" font-size="11" fill="#9d174d">찍나 (줌)</text>
</svg>
<figcaption>이 4가지 단서로 "터널이 화면 어디에 보일지"를 계산한다.</figcaption>
</figure>
---
## 3. 핵심 아이디어 — '창문에 스티커 붙이기' 🪟
**이 그림 하나만 이해하면 끝!** 가장 중요한 부분이에요.
내가 창문 안에서 밖의 나무를 봐요. **내 눈에서 나무로 직선**을 쭉 그으면,
그 선이 **창문 유리를 통과하는 점**이 있죠? 거기에 스티커를 붙이면 나무 위에 딱 겹쳐요!
<figure class="fig">
<svg viewBox="0 0 700 260" 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"/></marker></defs>
<!-- 눈 -->
<text x="70" y="135" text-anchor="middle" font-size="34">👁️</text>
<text x="70" y="170" text-anchor="middle" font-size="12" fill="#0f766e" font-weight="bold">카메라(눈)</text>
<!-- 나무 -->
<text x="620" y="120" text-anchor="middle" font-size="40">🌳</text>
<text x="620" y="160" text-anchor="middle" font-size="12" fill="#166534" font-weight="bold">터널(나무)</text>
<!-- 시선 -->
<line x1="95" y1="125" x2="600" y2="105" stroke="#0f766e" stroke-width="2.5" marker-end="url(#me)"/>
<!-- 창문 -->
<rect x="330" y="40" width="90" height="180" rx="6" fill="#e0f2fe" stroke="#0284c7" stroke-width="2" opacity="0.8"/>
<text x="375" y="238" text-anchor="middle" font-size="12" fill="#0369a1" font-weight="bold">창문 = 영상 화면</text>
<!-- 스티커 점 (시선이 창을 뚫는 곳) -->
<circle cx="375" cy="115" r="8" fill="#ef4444"/>
<text x="375" y="90" text-anchor="middle" font-size="12" fill="#b91c1c" font-weight="bold">스티커!</text>
<text x="375" y="105" text-anchor="middle" font-size="10" fill="#b91c1c">(이름표 붙는 자리)</text>
</svg>
<figcaption>카메라(눈)에서 터널(나무)로 선을 긋고, 그 선이 <b>화면(창문)을 뚫는 점</b>에 이름표를 붙인다.</figcaption>
</figure>
> 👉 그래서 하는 일은 딱 하나예요:
> **"카메라에서 터널로 선을 긋고, 그 선이 화면을 뚫는 점을 찾는다."**
---
## 4. 실제 계산은 3번에 나눠서 해요 ✏️
### 계산 ① 터널은 '나(드론) 기준으로' 어디 있지?
GPS 주소(위도·경도)는 **지구본 위 각도**라 계산이 불편해요.
그래서 먼저 **모눈종이(미터 단위)** 로 바꿔요. 그다음 드론을 중심(0,0)에 놓고 거리를 재요.
<figure class="fig">
<svg viewBox="0 0 700 230" role="img" aria-label="미터 좌표로 바꾸고 드론 기준 거리 재기">
<!-- 지구본 -->
<circle cx="90" cy="115" r="45" fill="#dbeafe" stroke="#2563eb"/>
<path d="M50,115 h80 M90,72 v86 M60,90 q30,25 60,0 M60,140 q30,-25 60,0" stroke="#2563eb" fill="none" stroke-width="1"/>
<text x="90" y="185" text-anchor="middle" font-size="11" fill="#1e3a8a">지구본 각도</text>
<text x="90" y="200" text-anchor="middle" font-size="10" fill="#1e3a8a">(위도·경도)</text>
<!-- 화살표 -->
<text x="185" y="118" text-anchor="middle" font-size="24" fill="#64748b">➡</text>
<text x="185" y="140" text-anchor="middle" font-size="10" fill="#64748b">바꾸기</text>
<!-- 모눈종이 -->
<g transform="translate(250,45)">
<rect x="0" y="0" width="160" height="140" fill="#fffdf8" stroke="#ca8a04"/>
<g stroke="#eadfae" stroke-width="1">
<line x1="32" y1="0" x2="32" y2="140"/><line x1="64" y1="0" x2="64" y2="140"/><line x1="96" y1="0" x2="96" y2="140"/><line x1="128" y1="0" x2="128" y2="140"/>
<line x1="0" y1="35" x2="160" y2="35"/><line x1="0" y1="70" x2="160" y2="70"/><line x1="0" y1="105" x2="160" y2="105"/>
</g>
<!-- 드론(중심) -->
<circle cx="48" cy="105" r="6" fill="#334155"/>
<text x="48" y="128" text-anchor="middle" font-size="10" fill="#334155">드론(0,0)</text>
<!-- 터널 -->
<text x="120" y="45" text-anchor="middle" font-size="16">🚇</text>
<!-- 거리 화살표 -->
<line x1="48" y1="105" x2="120" y2="105" stroke="#16a34a" stroke-width="1.5" stroke-dasharray="3 3"/>
<line x1="120" y1="105" x2="120" y2="45" stroke="#16a34a" stroke-width="1.5" stroke-dasharray="3 3"/>
</g>
<text x="470" y="80" font-size="13" fill="#166534" font-weight="bold">드론 기준으로 재보니…</text>
<text x="470" y="108" font-size="12" fill="#166534">• 앞으로 200 m</text>
<text x="470" y="130" font-size="12" fill="#166534">• 오른쪽으로 50 m</text>
<text x="470" y="152" font-size="12" fill="#166534">• 아래로 80 m</text>
</svg>
<figcaption>각도를 미터로 바꾼 뒤, 드론을 중심에 놓고 "앞/옆/아래로 몇 m"인지 잰다.</figcaption>
</figure>
### 계산 ② 그런데 내 카메라가 기울어져 있잖아
드론 카메라는 3가지로 방향이 틀어질 수 있어요. 사람이 고개를 움직이는 것과 똑같아요.
<figure class="fig">
<svg viewBox="0 0 700 180" role="img" aria-label="카메라 방향 3가지 yaw pitch roll">
<g transform="translate(120,90)">
<text x="0" y="-45" text-anchor="middle" font-size="30">🙂</text>
<path d="M-45,10 a45,18 0 0 0 90,0" fill="none" stroke="#2563eb" stroke-width="3"/>
<polygon points="45,10 38,3 38,17" fill="#2563eb"/>
<text x="0" y="45" text-anchor="middle" font-size="13" font-weight="bold" fill="#2563eb">yaw (좌우로 돌기)</text>
<text x="0" y="63" text-anchor="middle" font-size="10" fill="#64748b">고개 도리도리</text>
</g>
<g transform="translate(350,90)">
<text x="0" y="-45" text-anchor="middle" font-size="30">🙂</text>
<path d="M0,-30 a18,45 0 0 0 0,60" fill="none" stroke="#16a34a" stroke-width="3"/>
<polygon points="0,30 -7,23 7,23" fill="#16a34a"/>
<text x="0" y="45" text-anchor="middle" font-size="13" font-weight="bold" fill="#16a34a">pitch (위아래 숙이기)</text>
<text x="0" y="63" text-anchor="middle" font-size="10" fill="#64748b">고개 끄덕끄덕</text>
</g>
<g transform="translate(580,90)">
<text x="0" y="-45" text-anchor="middle" font-size="30">🙂</text>
<path d="M-30,0 a30,30 0 1 1 8,20" fill="none" stroke="#db2777" stroke-width="3"/>
<polygon points="-22,20 -30,14 -14,12" fill="#db2777"/>
<text x="0" y="45" text-anchor="middle" font-size="13" font-weight="bold" fill="#db2777">roll (옆으로 갸웃)</text>
<text x="0" y="63" text-anchor="middle" font-size="10" fill="#64748b">고개 갸우뚱</text>
</g>
</svg>
<figcaption>①에서 잰 위치를, 카메라가 실제로 보는 방향(yaw·pitch·roll)에 맞게 살짝 돌려준다.</figcaption>
</figure>
특히 우리 드론은 **앞을 비스듬히** 봐요(pitch). 그래서 터널이 화면 위쪽(멀리)에 작게 보일지, 아래쪽(가까이)에 크게 보일지가 딱 맞아떨어져요.
### 계산 ③ 3D를 납작한 화면에 눌러 담기
이제 3번의 **창문 스티커**를 실제로 계산해요. 멀수록 가운데로 작게, 가까울수록 크게·바깥으로!
<figure class="fig">
<svg viewBox="0 0 700 220" role="img" aria-label="원근 투영 - 멀면 작게 가까우면 크게">
<!-- 카메라 -->
<text x="55" y="120" text-anchor="middle" font-size="28">📷</text>
<!-- 시야 원뿔 -->
<polygon points="80,110 660,20 660,200" fill="#f1f5f9" stroke="#94a3b8" stroke-dasharray="4 4"/>
<!-- 화면 평면 -->
<rect x="250" y="55" width="14" height="110" fill="#e0f2fe" stroke="#0284c7"/>
<text x="257" y="185" text-anchor="middle" font-size="11" fill="#0369a1">화면</text>
<!-- 가까운 나무 (크게, 바깥) -->
<text x="330" y="80" text-anchor="middle" font-size="26">🌳</text>
<text x="330" y="100" text-anchor="middle" font-size="10" fill="#166534">가까움→크게</text>
<!-- 먼 나무 (작게, 가운데) -->
<text x="610" y="112" text-anchor="middle" font-size="15">🌲</text>
<text x="610" y="130" text-anchor="middle" font-size="10" fill="#166534">멀면→작게</text>
<!-- 화면 위 점 -->
<circle cx="257" cy="88" r="5" fill="#ef4444"/>
<circle cx="257" cy="112" r="4" fill="#ef4444"/>
</svg>
<figcaption>3D 방향을 납작한 화면 좌표로 바꾸면 → "화면 가로 60%, 세로 70%" 같은 <b>정확한 점</b>이 나온다.</figcaption>
</figure>
> 드디어 답: **"터널은 화면의 가로 60%, 세로 70% 지점에 보인다!"** → 거기에 이름표 딱! 🎯
---
## 5. 마지막 문제 — 드론이 흔들려요 🌬️
드론은 바람에 흔들려서, 그냥 두면 이름표가 **부들부들** 떨려요.
그래서 **여러 순간(프레임)의 위치를 평균 내서 부드럽게** 만들어요.
(친구가 손을 떨며 가리켜도, 우리가 "대충 저기구나" 하고 부드럽게 알아보는 것과 같아요.)
---
## 6. 그래서 '진짜 기술'은 뭘 썼을까? 🛠️
지금까지는 **비유**로 설명했어요. 이제 컴퓨터가 실제로 쓴 **진짜 도구(기술)** 이름을 알려줄게요.
어려운 이름이 나오지만, 오른쪽 칸을 보면 **한 줄로 쉽게** 이해돼요. 😊
<figure class="fig">
<svg viewBox="0 0 700 340" role="img" aria-label="비유와 진짜 기술 연결표">
<!-- 헤더 -->
<rect x="20" y="15" width="200" height="34" fill="#fef3c7" stroke="#ca8a04"/>
<rect x="220" y="15" width="230" height="34" fill="#e0e7ff" stroke="#4f46e5"/>
<rect x="450" y="15" width="230" height="34" fill="#dcfce7" stroke="#16a34a"/>
<text x="120" y="37" text-anchor="middle" font-size="12" font-weight="bold" fill="#854d0e">우리가 한 일(비유)</text>
<text x="335" y="37" text-anchor="middle" font-size="12" font-weight="bold" fill="#3730a3">진짜 기술 이름</text>
<text x="565" y="37" text-anchor="middle" font-size="12" font-weight="bold" fill="#166534">쉽게 말하면</text>
<!-- 행 -->
<g font-size="11">
<text x="30" y="70" fill="#444">지구본 각도 → 모눈종이</text>
<text x="230" y="70" fill="#3730a3" font-weight="bold">proj4 · EPSG:5186 좌표계</text>
<text x="460" y="70" fill="#166534">둥근 지구를 평평한 지도로 펴기</text>
<line x1="20" y1="80" x2="680" y2="80" stroke="#eee"/>
<text x="30" y="103" fill="#444">카메라 기울기 반영</text>
<text x="230" y="103" fill="#3730a3" font-weight="bold">회전행렬 (Rotation Matrix)</text>
<text x="460" y="103" fill="#166534">방향을 숫자표로 만들어 한 번에 돌리기</text>
<line x1="20" y1="113" x2="680" y2="113" stroke="#eee"/>
<text x="30" y="136" fill="#444">3D → 화면 점 (창문 스티커)</text>
<text x="230" y="136" fill="#3730a3" font-weight="bold">핀홀 카메라 모델 · 초점거리</text>
<text x="460" y="136" fill="#166534">바늘구멍 사진기 원리로 점 찍기</text>
<line x1="20" y1="146" x2="680" y2="146" stroke="#eee"/>
<text x="30" y="169" fill="#444">30장 사이 부드럽게</text>
<text x="230" y="169" fill="#3730a3" font-weight="bold">보간 (Interpolation)</text>
<text x="460" y="169" fill="#166534">사진 사이 중간 그림 상상해 채우기</text>
<line x1="20" y1="179" x2="680" y2="179" stroke="#eee"/>
<text x="30" y="202" fill="#444">흔들림 잡기</text>
<text x="230" y="202" fill="#3730a3" font-weight="bold">이동평균 · EMA 평활</text>
<text x="460" y="202" fill="#166534">여러 값 평균 내 부들거림 없애기</text>
<line x1="20" y1="212" x2="680" y2="212" stroke="#eee"/>
<text x="30" y="235" fill="#444">1초에 60번 다시 그리기</text>
<text x="230" y="235" fill="#3730a3" font-weight="bold">requestAnimationFrame · Canvas</text>
<text x="460" y="235" fill="#166534">화면 새로 칠할 때마다 그림 그리기</text>
<line x1="20" y1="245" x2="680" y2="245" stroke="#eee"/>
<text x="30" y="268" fill="#444">영상마다 프레임 수 맞추기</text>
<text x="230" y="268" fill="#3730a3" font-weight="bold">fps 자동 산출 (프레임수÷길이)</text>
<text x="460" y="268" fill="#166534">1초에 몇 장인지 스스로 알아내기</text>
<line x1="20" y1="278" x2="680" y2="278" stroke="#eee"/>
<text x="30" y="301" fill="#444">전체 화면·버튼·상태</text>
<text x="230" y="301" fill="#3730a3" font-weight="bold">React · TypeScript</text>
<text x="460" y="301" fill="#166534">화면을 블록처럼 조립하는 도구</text>
</g>
<rect x="20" y="15" width="660" height="300" fill="none" stroke="#ddd"/>
</svg>
<figcaption>왼쪽(비유) → 가운데(진짜 기술) → 오른쪽(쉬운 뜻). 이 표 한 장이 발표의 '기술 요약 슬라이드'예요.</figcaption>
</figure>
> 💡 **재미있는 사실:** 이 계산의 원래 버전은 **파이썬(Python)** 으로 만든 실험 프로그램(`advanced_tuner_v2.py`)이었어요.
> 그걸 **웹 브라우저에서 바로 돌아가게 TypeScript로 옮겨서**, 설치 없이 인터넷 창에서 실행돼요. 🌐
---
## 7. 중요한 기술 3개만 더 깊이 (그래도 쉽게!) 🔎
### 7-1. 둥근 지구를 평평하게 펴기 — 좌표계 변환 (proj4 · EPSG:5186)
지구는 **공(球)** 이라서, 위치를 "각도(위도·경도)"로 말해요. 그런데 각도로는 **"몇 미터 떨어졌지?"** 계산이 어려워요.
그래서 지구 표면을 **평평한 종이에 펴서(투영)**, 미터 단위로 바꿔요. 이때 쓰는 게 **proj4** 라는 도구고, 우리나라에 딱 맞게 만든 지도 규격이 **EPSG:5186 (한국 TM 좌표계)** 예요.
<figure class="fig">
<svg viewBox="0 0 700 180" role="img" aria-label="지구를 평평한 지도로 펴기">
<circle cx="120" cy="90" r="55" fill="#dbeafe" stroke="#2563eb" stroke-width="2"/>
<path d="M65,90 h110 M120,35 v110 M80,60 q40,30 80,0 M80,120 q40,-30 80,0" stroke="#2563eb" fill="none"/>
<text x="120" y="168" text-anchor="middle" font-size="11" fill="#1e3a8a">둥근 지구 (각도)</text>
<text x="300" y="85" text-anchor="middle" font-size="26" fill="#64748b">➡</text>
<text x="300" y="108" text-anchor="middle" font-size="11" fill="#64748b">proj4 로 펴기</text>
<g transform="translate(430,35)">
<rect x="0" y="0" width="220" height="110" fill="#fffdf8" stroke="#ca8a04"/>
<g stroke="#eadfae"><line x1="44" y1="0" x2="44" y2="110"/><line x1="88" y1="0" x2="88" y2="110"/><line x1="132" y1="0" x2="132" y2="110"/><line x1="176" y1="0" x2="176" y2="110"/><line x1="0" y1="37" x2="220" y2="37"/><line x1="0" y1="74" x2="220" y2="74"/></g>
</g>
<text x="540" y="168" text-anchor="middle" font-size="11" fill="#854d0e">평평한 지도 (미터) · EPSG:5186</text>
</svg>
<figcaption>각도로 된 위치를 '자로 잴 수 있는 미터'로 바꾸면, 드론과 터널 사이 거리를 정확히 계산할 수 있다.</figcaption>
</figure>
### 7-2. 방향을 '숫자표'로 만들어 한 번에 돌리기 — 회전행렬
카메라가 좌우(yaw)·위아래(pitch)·갸웃(roll)로 기울어져 있죠? 이 세 방향을 **하나씩 따로** 돌리면 복잡하고 실수해요.
그래서 수학에서는 방향 정보를 **작은 숫자표(행렬)** 로 만들어요. 이 숫자표를 위치에 **한 번 곱하면**, 세 방향 회전이 **동시에 딱** 적용돼요.
> 🎯 **비유:** 요리할 때 양념을 하나씩 넣는 대신, **미리 섞어둔 '만능 양념장' 한 숟갈**을 넣으면 끝나는 것과 같아요.
> 회전행렬 = "방향 돌리기 만능 양념장" 이에요. (우리 코드에선 `R_align · R_b2w^T` 라는 양념장을 써요.)
### 7-3. 바늘구멍 사진기 원리 — 핀홀 카메라 모델
3D 세상을 **납작한 사진 한 장**으로 만드는 원리예요. 옛날 **바늘구멍 사진기**를 떠올려요:
작은 구멍으로 빛이 들어와 뒤쪽 벽에 그림이 맺혀요. 이때 **멀리 있는 건 작게, 가까운 건 크게** 맺혀요(원근).
<figure class="fig">
<svg viewBox="0 0 700 210" role="img" aria-label="핀홀(바늘구멍) 카메라 원리">
<!-- 물체(터널): x=90, y=30~160 (가운데 y=95) -->
<line x1="90" y1="30" x2="90" y2="160" stroke="#16a34a" stroke-width="6"/>
<polygon points="90,30 84,44 96,44" fill="#16a34a"/>
<text x="90" y="180" text-anchor="middle" font-size="11" fill="#166534">터널(실제, 큼)</text>
<!-- 구멍 벽 + 바늘구멍(광선 교차점) at (360,95) -->
<line x1="360" y1="18" x2="360" y2="172" stroke="#94a3b8" stroke-width="6"/>
<circle cx="360" cy="95" r="5.5" fill="#111"/>
<text x="360" y="192" text-anchor="middle" font-size="11" fill="#475569">바늘구멍(=렌즈)</text>
<!-- 광선: 물체 양끝 → 바늘구멍(360,95) 통과 → 화면. 반드시 점을 지나 X자로 교차. -->
<line x1="90" y1="30" x2="560" y2="143.1" stroke="#f59e0b" stroke-width="1.5"/>
<line x1="90" y1="160" x2="560" y2="46.9" stroke="#f59e0b" stroke-width="1.5"/>
<!-- 화면에 맺힌 상: x=560, 거꾸로 + 작게 (y=46.9~143.1, 가운데 95) -->
<line x1="560" y1="46.9" x2="560" y2="143.1" stroke="#ef4444" stroke-width="5"/>
<polygon points="560,143.1 554,129.1 566,129.1" fill="#ef4444"/>
<text x="560" y="180" text-anchor="middle" font-size="11" fill="#b91c1c">맺힌 상(작고 거꾸로 ↕)</text>
</svg>
<figcaption>구멍(렌즈)을 지나며 3D가 화면에 맺힌다. '얼마나 당겨 찍나(초점거리)'로 크기가 정해진다.</figcaption>
</figure>
컴퓨터는 이 원리를 **곱셈·나눗셈 몇 번**으로 계산해서, "터널이 화면의 정확히 어느 점"인지 구해요.
여기서 **초점거리(focal length)** 가 클수록(망원처럼 당길수록) 물체가 화면에서 크게 보여요.
---
## 8. 한 장 요약 (마지막 슬라이드) 🎁
<figure class="fig">
<svg viewBox="0 0 700 300" role="img" aria-label="전체 요약 파이프라인">
<defs><marker id="ms" markerWidth="12" markerHeight="12" refX="8" refY="4" orient="auto"><path d="M0,0 L8,4 L0,8 Z" fill="#7c3aed"/></marker></defs>
<rect x="150" y="15" width="400" height="42" rx="8" fill="#f5f3ff" stroke="#7c3aed"/>
<text x="350" y="41" text-anchor="middle" font-size="13" font-weight="bold" fill="#5b21b6">드론 위치 + 보는 방향 + 터널 위치 + 렌즈 정보</text>
<line x1="350" y1="57" x2="350" y2="80" stroke="#7c3aed" stroke-width="2" marker-end="url(#ms)"/>
<rect x="150" y="82" width="400" height="42" rx="8" fill="#ecfdf5" stroke="#16a34a"/>
<text x="350" y="108" text-anchor="middle" font-size="13" font-weight="bold" fill="#166534">① 미터로 바꿔 "드론 기준 위치" 재기</text>
<line x1="350" y1="124" x2="350" y2="147" stroke="#7c3aed" stroke-width="2" marker-end="url(#ms)"/>
<rect x="150" y="149" width="400" height="42" rx="8" fill="#eff6ff" stroke="#2563eb"/>
<text x="350" y="175" text-anchor="middle" font-size="13" font-weight="bold" fill="#1e3a8a">② 카메라 기울기만큼 방향 돌리기</text>
<line x1="350" y1="191" x2="350" y2="214" stroke="#7c3aed" stroke-width="2" marker-end="url(#ms)"/>
<rect x="150" y="216" width="400" height="42" rx="8" fill="#fefce8" stroke="#ca8a04"/>
<text x="350" y="242" text-anchor="middle" font-size="13" font-weight="bold" fill="#854d0e">③ 창문에 스티커 붙이듯 화면 점 찾기</text>
<line x1="350" y1="258" x2="350" y2="278" stroke="#7c3aed" stroke-width="2" marker-end="url(#ms)"/>
<text x="350" y="294" text-anchor="middle" font-size="14" font-weight="bold" fill="#b91c1c">🎯 화면의 정확한 자리에 이름표 붙이기!</text>
</svg>
</figure>
> **한 문장 결론:**
> **"드론이 어디서·어느 쪽을 보는지 알기 때문에, 지도 속 터널이 영상 화면 어디에 보일지를 계산해서 이름표를 딱 붙이는 기술"** 이에요.
이 "드론 좌표 → 영상 픽셀 3D 투영"이 GhiVideo를 **보통 영상 플레이어와 다르게 만드는 가장 특별한 기술**이랍니다. ✨