kcg-monitoring/frontend/src/components/layers/SeismicMarker.tsx
htlee 7fa4e2bfb1 feat: 센서 그래프 개선 + 지진 마커 + 시설 아이콘 정렬 + SSH 재시도 v2
- SensorChart: 히스토리 1H/2H/3H/6H, 기압 SLP 보정, 데이터 범위 확장(y축 시작)
- SensorChart Tooltip: KST 시간 포맷, 위치 상단 고정, 스타일 통일
- 지진 포인트 클릭 → 지도 flyTo + SeismicMarker 진도별 펄스 원형 표시
- SatelliteMap flyTo 지원 추가
- OilFacilityLayer: planned ring SVG 내부로 이동 (아이콘 중심 정렬 수정)
- 밝은 테마 text-shadow CSS 변수 분리 (dark/light)
- deploy.yml: SSH SCP+실행 각 3회 재시도 (kex_exchange 거부 대응)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 11:02:55 +09:00

83 lines
2.5 KiB
TypeScript

import { Marker } from 'react-map-gl/maplibre';
import './SeismicMarker.css';
interface Props {
lat: number;
lng: number;
magnitude: number;
place: string;
}
/**
* 진도 기반 영향 반경 (km → px 근사, zoom 8 기준)
* M2: ~5km, M3: ~15km, M4: ~40km, M5: ~100km, M6: ~200km
* zoom 8에서 약 1km ≈ 0.6px
*/
function getRadiusPx(magnitude: number): number {
const radiusKm = Math.pow(10, 0.5 * magnitude - 0.5);
return Math.max(20, Math.min(radiusKm * 0.6, 200));
}
function getMagnitudeColor(magnitude: number): string {
if (magnitude < 3) return 'rgba(251, 191, 36, 0.4)'; // yellow
if (magnitude < 4) return 'rgba(249, 115, 22, 0.4)'; // orange
if (magnitude < 5) return 'rgba(239, 68, 68, 0.4)'; // red
if (magnitude < 6) return 'rgba(220, 38, 38, 0.5)'; // dark red
return 'rgba(153, 27, 27, 0.6)'; // maroon
}
function getStrokeColor(magnitude: number): string {
if (magnitude < 3) return '#fbbf24';
if (magnitude < 4) return '#f97316';
if (magnitude < 5) return '#ef4444';
if (magnitude < 6) return '#dc2626';
return '#991b1b';
}
export function SeismicMarker({ lat, lng, magnitude, place }: Props) {
const size = getRadiusPx(magnitude) * 2;
const color = getMagnitudeColor(magnitude);
const stroke = getStrokeColor(magnitude);
return (
<Marker longitude={lng} latitude={lat} anchor="center">
<div className="seismic-marker-container" style={{ width: size, height: size }}>
{/* Outer pulse ring */}
<div
className="seismic-pulse-ring"
style={{
width: size,
height: size,
borderColor: stroke,
}}
/>
{/* Inner pulse ring */}
<div
className="seismic-pulse-ring seismic-pulse-ring-inner"
style={{
width: size * 0.6,
height: size * 0.6,
borderColor: stroke,
}}
/>
{/* Fill circle */}
<div
className="seismic-fill"
style={{
width: size,
height: size,
background: `radial-gradient(circle, ${color} 0%, transparent 70%)`,
}}
/>
{/* Center dot */}
<div className="seismic-center-dot" style={{ background: stroke }} />
{/* Label */}
<div className="seismic-label" style={{ color: stroke }}>
<span className="seismic-magnitude">M{magnitude.toFixed(1)}</span>
<span className="seismic-place">{place}</span>
</div>
</div>
</Marker>
);
}