- {active.map(k => {
- const color = FILTER_COLOR[k];
- const count = filterCount[k] ?? 0;
- return (
-
-
{FILTER_ICON[k]}
- {t(FILTER_I18N_KEY[k])}
-
{count}์ฒ
+ <>
+
+ {active.map(k => {
+ const color = FILTER_COLOR[k];
+ const count = getShipsForFilter(k).length;
+ const isOpen = activeBadgeFilter === k;
+ return (
+
setActiveBadgeFilter(prev => prev === k ? null : k)}
+ >
+ {FILTER_ICON[k]}
+ {FILTER_I18N_KEY[k] ? t(FILTER_I18N_KEY[k]) : k}
+ {count}์ฒ
+
+ );
+ })}
+
+ {activeBadgeFilter && badgeShips.length > 0 && (
+
+
+
+ {FILTER_ICON[activeBadgeFilter]} {FILTER_I18N_KEY[activeBadgeFilter] ? t(FILTER_I18N_KEY[activeBadgeFilter]) : activeBadgeFilter} โ {badgeShips.length}์ฒ
+
+
+
+
+
- );
- })}
-
+
+
+
+
+ | MMSI |
+ Name |
+ Flag |
+ Type |
+ Speed |
+
+
+
+ {badgeShips.slice(0, 200).map(s => (
+ setFlyToTarget({ lng: s.lng, lat: s.lat, zoom: 12 })}
+ >
+ | {s.mmsi} |
+ {s.name || '-'} |
+ {s.flag || '??'} |
+ {s.mtCategory || '-'} |
+ {s.speed?.toFixed(1)}kn |
+
+ ))}
+
+
+ {badgeShips.length > 200 &&
...์ธ {badgeShips.length - 200}์ฒ
}
+
+
+ )}
+ >
);
})()}
diff --git a/frontend/src/hooks/useKoreaFilters.ts b/frontend/src/hooks/useKoreaFilters.ts
index 5a7dcde..e5a679d 100644
--- a/frontend/src/hooks/useKoreaFilters.ts
+++ b/frontend/src/hooks/useKoreaFilters.ts
@@ -12,6 +12,7 @@ interface KoreaFilters {
cableWatch: boolean;
dokdoWatch: boolean;
ferryWatch: boolean;
+ cnFishing: boolean;
}
interface DokdoAlert {
@@ -28,6 +29,7 @@ interface UseKoreaFiltersResult {
transshipSuspects: Set
;
cableWatchSuspects: Set;
dokdoWatchSuspects: Set;
+ cnFishingSuspects: Set;
dokdoAlerts: DokdoAlert[];
anyFilterOn: boolean;
}
@@ -43,7 +45,6 @@ export function useKoreaFilters(
visibleShips: Ship[],
currentTime: number,
analysisMap?: Map,
- cnFishingOn = false,
): UseKoreaFiltersResult {
const [filters, setFilters] = useLocalStorage('koreaFilters', {
illegalFishing: false,
@@ -52,6 +53,7 @@ export function useKoreaFilters(
cableWatch: false,
dokdoWatch: false,
ferryWatch: false,
+ cnFishing: false,
});
const [dokdoAlerts, setDokdoAlerts] = useState([]);
@@ -70,7 +72,7 @@ export function useKoreaFilters(
filters.cableWatch ||
filters.dokdoWatch ||
filters.ferryWatch ||
- cnFishingOn;
+ filters.cnFishing;
// ๋ถ๋ฒํ์ ์์ฌ ์ ๋ฐ ํ์ง (Python ๋ถ์ ๊ฒฐ๊ณผ ์๋น)
const transshipSuspects = useMemo(() => {
@@ -250,6 +252,18 @@ export function useKoreaFilters(
return result;
}, [koreaShips, filters.dokdoWatch, currentTime]);
+ // ์ค๊ตญ์ด์ ์์ฌ ์ ๋ฐ Set
+ const cnFishingSuspects = useMemo(() => {
+ if (!filters.cnFishing) return new Set();
+ const result = new Set();
+ for (const s of koreaShips) {
+ const isCnFishing = s.flag === 'CN' && s.mtCategory === 'fishing';
+ const isGearPattern = /^.+?_\d+_\d+_?$/.test(s.name || '');
+ if (isCnFishing || isGearPattern) result.add(s.mmsi);
+ }
+ return result;
+ }, [filters.cnFishing, koreaShips]);
+
// ํํฐ๋ง๋ ์ ๋ฐ ๋ชฉ๋ก
const filteredShips = useMemo(() => {
if (!anyFilterOn) return visibleShips;
@@ -272,14 +286,10 @@ export function useKoreaFilters(
if (filters.cableWatch && cableWatchSet.has(s.mmsi)) return true;
if (filters.dokdoWatch && dokdoWatchSet.has(s.mmsi)) return true;
if (filters.ferryWatch && s.mtCategory === 'passenger') return true;
- if (cnFishingOn) {
- const isCnFishing = s.flag === 'CN' && s.mtCategory === 'fishing';
- const isGearPattern = /^.+?_\d+_\d+_?$/.test(s.name || '');
- if (isCnFishing || isGearPattern) return true;
- }
+ if (filters.cnFishing && cnFishingSuspects.has(s.mmsi)) return true;
return false;
});
- }, [visibleShips, filters, anyFilterOn, transshipSuspects, darkVesselSet, cableWatchSet, dokdoWatchSet, analysisMap, cnFishingOn]);
+ }, [visibleShips, filters, anyFilterOn, transshipSuspects, darkVesselSet, cableWatchSet, dokdoWatchSet, analysisMap, cnFishingSuspects]);
return {
filters,
@@ -288,6 +298,7 @@ export function useKoreaFilters(
transshipSuspects,
cableWatchSuspects: cableWatchSet,
dokdoWatchSuspects: dokdoWatchSet,
+ cnFishingSuspects,
dokdoAlerts,
anyFilterOn,
};