- WGS84 사전 변환 GeoJSON 생성 (런타임 변환 제거) - FishingZoneLayer: 수역별 색상 fill/line + 이름 라벨 - AnalysisOverlay: 마커 크기 확대, 한글 라벨, 선박명 표시 - fishingAnalysis.ts: EPSG:3857 변환 로직 제거, WGS84 JSON 직접 사용 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
94 lines
2.7 KiB
TypeScript
94 lines
2.7 KiB
TypeScript
import { useMemo } from 'react';
|
|
import { Source, Layer, Marker } from 'react-map-gl/maplibre';
|
|
import fishingZonesData from '../../data/zones/fishing-zones-wgs84.json';
|
|
|
|
const ZONE_FILL: Record<string, string> = {
|
|
ZONE_I: 'rgba(59, 130, 246, 0.15)',
|
|
ZONE_II: 'rgba(16, 185, 129, 0.15)',
|
|
ZONE_III: 'rgba(245, 158, 11, 0.15)',
|
|
ZONE_IV: 'rgba(239, 68, 68, 0.15)',
|
|
};
|
|
|
|
const ZONE_LINE: Record<string, string> = {
|
|
ZONE_I: 'rgba(59, 130, 246, 0.6)',
|
|
ZONE_II: 'rgba(16, 185, 129, 0.6)',
|
|
ZONE_III: 'rgba(245, 158, 11, 0.6)',
|
|
ZONE_IV: 'rgba(239, 68, 68, 0.6)',
|
|
};
|
|
|
|
/** 폴리곤 중심점 (좌표 평균) */
|
|
function centroid(coordinates: number[][][][]): [number, number] {
|
|
let sLng = 0, sLat = 0, n = 0;
|
|
for (const poly of coordinates) {
|
|
for (const ring of poly) {
|
|
for (const [lng, lat] of ring) {
|
|
sLng += lng; sLat += lat; n++;
|
|
}
|
|
}
|
|
}
|
|
return n > 0 ? [sLng / n, sLat / n] : [0, 0];
|
|
}
|
|
|
|
const fillColor = [
|
|
'match', ['get', 'id'],
|
|
'ZONE_I', ZONE_FILL.ZONE_I,
|
|
'ZONE_II', ZONE_FILL.ZONE_II,
|
|
'ZONE_III', ZONE_FILL.ZONE_III,
|
|
'ZONE_IV', ZONE_FILL.ZONE_IV,
|
|
'rgba(0,0,0,0)',
|
|
] as maplibregl.ExpressionSpecification;
|
|
|
|
const lineColor = [
|
|
'match', ['get', 'id'],
|
|
'ZONE_I', ZONE_LINE.ZONE_I,
|
|
'ZONE_II', ZONE_LINE.ZONE_II,
|
|
'ZONE_III', ZONE_LINE.ZONE_III,
|
|
'ZONE_IV', ZONE_LINE.ZONE_IV,
|
|
'rgba(0,0,0,0)',
|
|
] as maplibregl.ExpressionSpecification;
|
|
|
|
export function FishingZoneLayer() {
|
|
const labels = useMemo(() =>
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
fishingZonesData.features.map((f: any) => {
|
|
const [lng, lat] = centroid(f.geometry.coordinates);
|
|
return { id: f.properties.id as string, name: f.properties.name as string, lng, lat };
|
|
}), []);
|
|
|
|
return (
|
|
<>
|
|
<Source id="fishing-zones" type="geojson" data={fishingZonesData as GeoJSON.FeatureCollection}>
|
|
<Layer
|
|
id="fishing-zone-fill"
|
|
type="fill"
|
|
paint={{ 'fill-color': fillColor, 'fill-opacity': 1 }}
|
|
/>
|
|
<Layer
|
|
id="fishing-zone-line"
|
|
type="line"
|
|
paint={{
|
|
'line-color': lineColor,
|
|
'line-opacity': 1,
|
|
'line-width': 1.5,
|
|
'line-dasharray': [4, 2],
|
|
}}
|
|
/>
|
|
</Source>
|
|
|
|
{labels.map(({ id, name, lng, lat }) => (
|
|
<Marker key={`zone-${id}`} longitude={lng} latitude={lat} anchor="center">
|
|
<div style={{
|
|
fontSize: 10, fontWeight: 700, color: '#fff',
|
|
textShadow: '0 0 3px #000, 0 0 3px #000',
|
|
backgroundColor: 'rgba(0,0,0,0.45)',
|
|
borderRadius: 3, padding: '1px 5px',
|
|
whiteSpace: 'nowrap', pointerEvents: 'none',
|
|
}}>
|
|
{name}
|
|
</div>
|
|
</Marker>
|
|
))}
|
|
</>
|
|
);
|
|
}
|