feat: RoutePanel 시점/종점 표시 및 교량/터널만 표시
- 상단 시점, 하단 종점 측점명 고정 표시 - POI는 교량/터널만 필터링 (터널: 보라, 교량: 하늘색) - 불필요한 측점 도트/텍스트 제거 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -225,6 +225,14 @@ export default function RoutePanel({ currentTime, visible, onSeek }: RoutePanelP
|
|||||||
const maxKm = Math.max(...allKms);
|
const maxKm = Math.max(...allKms);
|
||||||
const kmToY = (km: number) => (1 - (km - minKm) / (maxKm - minKm)) * 100;
|
const kmToY = (km: number) => (1 - (km - minKm) / (maxKm - minKm)) * 100;
|
||||||
|
|
||||||
|
// 시점/종점 측점
|
||||||
|
const sortedByKm = [...validStations].sort((a, b) => stationKm(a.title) - stationKm(b.title));
|
||||||
|
const startStation = sortedByKm[0];
|
||||||
|
const endStation = sortedByKm[sortedByKm.length - 1];
|
||||||
|
|
||||||
|
// 교량/터널만 표시
|
||||||
|
const filteredPois = pois.filter(p => p.category === '\uD130\uB110' || p.category === '\uAD50\uB7C9');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={panelRef}
|
ref={panelRef}
|
||||||
@@ -233,49 +241,56 @@ export default function RoutePanel({ currentTime, visible, onSeek }: RoutePanelP
|
|||||||
>
|
>
|
||||||
{/* Center vertical line */}
|
{/* Center vertical line */}
|
||||||
<div
|
<div
|
||||||
className="absolute top-0 bottom-0"
|
className="absolute"
|
||||||
style={{ left: 36, width: 2, background: 'rgba(255,255,255,0.3)' }}
|
style={{ left: 36, width: 2, top: 20, bottom: 20, background: 'rgba(255,255,255,0.3)' }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Stations */}
|
{/* 시점 label — top */}
|
||||||
{validStations.map((st, i) => {
|
<div className="absolute left-0 right-0 flex items-center" style={{ top: 4 }}>
|
||||||
const km = stationKm(st.title);
|
<div className="text-[9px] text-white/50 text-right" style={{ width: 34 }}>
|
||||||
if (km < 0) return null;
|
{startStation.title}
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={`st-${i}`}
|
|
||||||
className="absolute flex items-center"
|
|
||||||
style={{ top: `${kmToY(km)}%`, transform: 'translateY(-50%)', left: 0, right: 0 }}
|
|
||||||
>
|
|
||||||
<div className="text-[9px] text-white/70 text-right" style={{ width: 34 }}>
|
|
||||||
{st.title}
|
|
||||||
</div>
|
</div>
|
||||||
<div style={{ position: 'absolute', left: 33, width: 8, display: 'flex', justifyContent: 'center' }}>
|
<div style={{ position: 'absolute', left: 30, width: 12, display: 'flex', justifyContent: 'center' }}>
|
||||||
<div className="w-2 h-2 rounded-full bg-white/60" />
|
<div className="w-2 h-2 rounded-full bg-white/50" />
|
||||||
</div>
|
</div>
|
||||||
|
<div className="text-[8px] text-white/40 ml-1" style={{ position: 'absolute', left: 44 }}>시점</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
|
||||||
})}
|
|
||||||
|
|
||||||
{/* POIs */}
|
{/* 종점 label — bottom */}
|
||||||
{pois.map((poi, i) => {
|
<div className="absolute left-0 right-0 flex items-center" style={{ bottom: 4 }}>
|
||||||
|
<div className="text-[9px] text-white/50 text-right" style={{ width: 34 }}>
|
||||||
|
{endStation.title}
|
||||||
|
</div>
|
||||||
|
<div style={{ position: 'absolute', left: 30, width: 12, display: 'flex', justifyContent: 'center' }}>
|
||||||
|
<div className="w-2 h-2 rounded-full bg-white/50" />
|
||||||
|
</div>
|
||||||
|
<div className="text-[8px] text-white/40" style={{ position: 'absolute', left: 44 }}>종점</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 교량/터널 POIs */}
|
||||||
|
{filteredPois.map((poi, i) => {
|
||||||
const km = poiKm(poi, validStations);
|
const km = poiKm(poi, validStations);
|
||||||
if (km < 0) return null;
|
if (km < 0) return null;
|
||||||
|
const y = kmToY(km);
|
||||||
|
if (y < 5 || y > 95) return null; // 시점/종점 영역과 겹치지 않게
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={`poi-${i}`}
|
key={`poi-${i}`}
|
||||||
className="absolute flex items-center"
|
className="absolute flex items-center pointer-events-none"
|
||||||
style={{ top: `${kmToY(km)}%`, transform: 'translateY(-50%)', left: 0, right: 0 }}
|
style={{ top: `${y}%`, transform: 'translateY(-50%)', left: 0, right: 0 }}
|
||||||
>
|
>
|
||||||
<div className="text-[11px] text-right" style={{ width: 34 }}>
|
<div style={{ position: 'absolute', left: 30, width: 12, display: 'flex', justifyContent: 'center' }}>
|
||||||
{CATEGORY_EMOJI[poi.category] || '\uD83D\uDCCD'}
|
<div
|
||||||
</div>
|
className="w-2.5 h-2.5 rounded-sm"
|
||||||
<div style={{ position: 'absolute', left: 33, width: 8, display: 'flex', justifyContent: 'center' }}>
|
style={{ background: poi.category === '\uD130\uB110' ? '#6366f1' : '#0ea5e9' }}
|
||||||
<div className="w-1.5 h-1.5 rounded-full bg-cyan-400/70" />
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="text-[8px] text-cyan-300/80 truncate"
|
className="text-[8px] truncate"
|
||||||
style={{ position: 'absolute', left: 44, right: 2 }}
|
style={{
|
||||||
|
position: 'absolute', left: 44, right: 2,
|
||||||
|
color: poi.category === '\uD130\uB110' ? '#a5b4fc' : '#7dd3fc',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{poi.title}
|
{poi.title}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user