diff --git a/frontend/src/components/korea/FleetClusterLayer.tsx b/frontend/src/components/korea/FleetClusterLayer.tsx index 920bc45..9163531 100644 --- a/frontend/src/components/korea/FleetClusterLayer.tsx +++ b/frontend/src/components/korea/FleetClusterLayer.tsx @@ -1,5 +1,5 @@ import { useState, useEffect, useMemo, useCallback } from 'react'; -import { Source, Layer } from 'react-map-gl/maplibre'; +import { Source, Layer, Marker } from 'react-map-gl/maplibre'; import type { GeoJSON } from 'geojson'; import type { Ship, VesselAnalysisDto } from '../../types'; import { fetchFleetCompanies } from '../../services/vesselAnalysis'; @@ -89,6 +89,7 @@ export function FleetClusterLayer({ ships, analysisMap, clusters, onShipSelect, const [expandedFleet, setExpandedFleet] = useState(null); const [hoveredFleetId, setHoveredFleetId] = useState(null); const [expandedGearGroup, setExpandedGearGroup] = useState(null); + const [selectedGearGroup, setSelectedGearGroup] = useState(null); useEffect(() => { fetchFleetCompanies().then(setCompanies).catch(() => {}); @@ -199,6 +200,8 @@ export function FleetClusterLayer({ ships, analysisMap, clusters, onShipSelect, }, [gearGroupMap]); const handleGearGroupZoom = useCallback((parentName: string) => { + setSelectedGearGroup(prev => prev === parentName ? null : parentName); + setExpandedGearGroup(parentName); const entry = gearGroupMap.get(parentName); if (!entry) return; const all: Ship[] = [...entry.gears]; @@ -383,6 +386,60 @@ export function FleetClusterLayer({ ships, analysisMap, clusters, onShipSelect, /> + {/* 선택된 어구 그룹 하이라이트 + 모선 마커 */} + {selectedGearGroup && (() => { + const entry = gearGroupMap.get(selectedGearGroup); + if (!entry) return null; + const points: [number, number][] = entry.gears.map(g => [g.lng, g.lat]); + if (entry.parent) points.push([entry.parent.lng, entry.parent.lat]); + + const hlFeatures: GeoJSON.Feature[] = []; + if (points.length >= 3) { + const hull = convexHull(points); + const padded = padPolygon(hull, 0.01); + padded.push(padded[0]); + hlFeatures.push({ + type: 'Feature', + properties: {}, + geometry: { type: 'Polygon', coordinates: [padded] }, + }); + } + const hlGeoJson: GeoJSON.FeatureCollection = { type: 'FeatureCollection', features: hlFeatures }; + + return ( + <> + {hlFeatures.length > 0 && ( + + + + + )} + {entry.parent && ( + +
+ M +
+
+ {entry.parent.name || entry.parent.mmsi} +
+
+ )} + + ); + })()} + {/* 비허가 어구 클러스터 폴리곤 */}