From c2ca830ef00edfee7d031a650b06340bf5b4eab9 Mon Sep 17 00:00:00 2001 From: htlee Date: Mon, 16 Feb 2026 23:20:47 +0900 Subject: [PATCH] =?UTF-8?q?chore:=20=EB=AF=B8=EC=82=AC=EC=9A=A9=20?= =?UTF-8?q?=EB=8D=B0=EB=93=9C=EC=BD=94=EB=93=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - entities/vessel/lib/filter.ts (미사용 필터 유틸) - entities/vessel/model/mockFleet.ts (미사용 mock 데이터) - shared/lib/color/hexToRgb.ts (MapSettingsPanel 로컬 중복) Co-Authored-By: Claude Opus 4.6 --- apps/web/src/entities/vessel/lib/filter.ts | 34 --- .../src/entities/vessel/model/mockFleet.ts | 278 ------------------ apps/web/src/shared/lib/color/hexToRgb.ts | 7 - 3 files changed, 319 deletions(-) delete mode 100644 apps/web/src/entities/vessel/lib/filter.ts delete mode 100644 apps/web/src/entities/vessel/model/mockFleet.ts delete mode 100644 apps/web/src/shared/lib/color/hexToRgb.ts diff --git a/apps/web/src/entities/vessel/lib/filter.ts b/apps/web/src/entities/vessel/lib/filter.ts deleted file mode 100644 index 516c0dc..0000000 --- a/apps/web/src/entities/vessel/lib/filter.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { Vessel, VesselTypeCode } from "../model/types"; - -export function isTrawl(code: VesselTypeCode) { - return code === "PT" || code === "PT-S"; -} - -export function filterVesselsForMap(vessels: readonly Vessel[], selectedType: VesselTypeCode | null) { - if (!selectedType) return vessels; - - return vessels.filter((v) => { - if (v.code === selectedType) return true; - - // PT and PT-S should be shown together - if (selectedType === "PT" && v.code === "PT-S") return true; - if (selectedType === "PT-S" && v.code === "PT") return true; - - // FC interacts with trawl; show trawl when FC selected and FC when trawl selected - if (selectedType === "FC" && (v.code === "PT" || v.code === "PT-S")) return true; - if ((selectedType === "PT" || selectedType === "PT-S") && v.code === "FC") return true; - - return false; - }); -} - -export function filterVesselsForList(vessels: readonly Vessel[], selectedType: VesselTypeCode | null) { - if (!selectedType) return vessels; - return vessels.filter((v) => { - if (v.code === selectedType) return true; - if (selectedType === "PT" && v.code === "PT-S") return true; - if (selectedType === "PT-S" && v.code === "PT") return true; - return false; - }); -} - diff --git a/apps/web/src/entities/vessel/model/mockFleet.ts b/apps/web/src/entities/vessel/model/mockFleet.ts deleted file mode 100644 index 8e86e37..0000000 --- a/apps/web/src/entities/vessel/model/mockFleet.ts +++ /dev/null @@ -1,278 +0,0 @@ -import type { ZoneId } from "../../zone/model/meta"; -import { haversineNm } from "../../../shared/lib/geo/haversineNm"; -import { VESSEL_TYPES } from "./meta"; -import type { FleetOwner, FleetState, TrawlPair, Vessel, VesselTypeCode } from "./types"; - -const SURNAMES = ["张", "王", "李", "刘", "陈", "杨", "黄", "赵", "周", "吴", "徐", "孙", "马", "朱", "胡", "郭", "林", "何", "高", "罗"]; -const REGIONS = ["荣成", "石岛", "烟台", "威海", "日照", "青岛", "连云港", "舟山", "象山", "大连"]; - -const ZONE_BOUNDS: Record = { - "1": { lon: [128.85, 131.70], lat: [36.16, 38.25] }, - "2": { lon: [126.00, 128.90], lat: [32.18, 34.35] }, - "3": { lon: [124.12, 126.06], lat: [33.13, 35.00] }, - "4": { lon: [124.33, 125.85], lat: [35.00, 37.00] }, -}; - -function rnd(a: number, b: number) { - return a + Math.random() * (b - a); -} - -function pick(arr: readonly T[]) { - return arr[Math.floor(Math.random() * arr.length)]; -} - -function randomPointInZone(zone: ZoneId) { - const b = ZONE_BOUNDS[zone]; - // Small margin to avoid sitting exactly on the edge. - const lat = rnd(b.lat[0] + 0.05, b.lat[1] - 0.05); - const lon = rnd(b.lon[0] + 0.05, b.lon[1] - 0.05); - return { lat, lon }; -} - -function makePermit(id: number, suffix: "A" | "B") { - return `C21-${10000 + id}${suffix}`; -} - -export function createMockFleetState(): FleetState { - const vessels: Vessel[] = []; - const owners: FleetOwner[] = []; - const ptPairs: TrawlPair[] = []; - - let vid = 1; - - // PT pairs: PT count == PT-S count, treat as "pair count". - for (let i = 0; i < VESSEL_TYPES.PT.count; i++) { - const owner = `${pick(SURNAMES)}${pick(SURNAMES)}${pick(["渔业", "海产", "水产", "船务"])}${pick(["有限公司", "合作社", ""])}`; - const region = pick(REGIONS); - const zone = pick(["2", "3"]); - const { lat, lon } = randomPointInZone(zone); - - const isFishing = Math.random() < 0.55; - const sp = isFishing ? rnd(2.5, 4.5) : rnd(6, 11); - const crs = rnd(0, 360); - - const pairDist = isFishing ? rnd(0.2, 1.2) : rnd(0, 0.3); // NM (rough) - const pairAngle = rnd(0, 360); - const lat2 = lat + (pairDist / 60) * Math.cos((pairAngle * Math.PI) / 180); - const lon2 = lon + ((pairDist / 60) * Math.sin((pairAngle * Math.PI) / 180)) / Math.cos((lat * Math.PI) / 180); - - const permitBase = vid; - const ptId = vid++; - const ptsId = vid++; - - const pt: Vessel = { - id: ptId, - permit: makePermit(permitBase, "A"), - code: "PT", - color: VESSEL_TYPES.PT.color, - lat, - lon, - speed: Number(sp.toFixed(1)), - course: Number(crs.toFixed(0)), - state: isFishing ? "조업중" : sp < 1 ? "정박" : "항해중", - zone, - isFishing, - owner, - region, - pairId: null, - pairDistNm: Number(pairDist.toFixed(2)), - nearVesselIds: [], - }; - - const pts: Vessel = { - id: ptsId, - permit: makePermit(permitBase, "B"), - code: "PT-S", - color: VESSEL_TYPES["PT-S"].color, - lat: Number(lat2.toFixed(4)), - lon: Number(lon2.toFixed(4)), - speed: Number((sp + rnd(-0.3, 0.3)).toFixed(1)), - course: Number((crs + rnd(-10, 10)).toFixed(0)), - state: isFishing ? "조업중" : sp < 1 ? "정박" : "항해중", - zone, - isFishing, - owner, - region, - pairId: null, - pairDistNm: pt.pairDistNm, - nearVesselIds: [], - }; - - pt.pairId = pts.id; - pts.pairId = pt.id; - - vessels.push(pt, pts); - ptPairs.push({ mainId: pt.id, subId: pts.id, owner, region }); - owners.push({ name: owner, region, vessels: [pt.id, pts.id], type: "trawl" }); - } - - // GN vessels - for (let i = 0; i < VESSEL_TYPES.GN.count; i++) { - const attachToOwner = Math.random() < 0.3 ? owners[Math.floor(Math.random() * owners.length)] : null; - const owner = attachToOwner ? attachToOwner.name : `${pick(SURNAMES)}${pick(SURNAMES)}${pick(["渔业", "水产"])}有限公司`; - const region = attachToOwner ? attachToOwner.region : pick(REGIONS); - const zone = pick(["2", "3", "4"]); - const { lat, lon } = randomPointInZone(zone); - - const isFishing = Math.random() < 0.5; - const sp = isFishing ? rnd(0.5, 2) : rnd(5, 10); - - const id = vid++; - const v: Vessel = { - id, - permit: makePermit(id, "A"), - code: "GN", - color: VESSEL_TYPES.GN.color, - lat, - lon, - speed: Number(sp.toFixed(1)), - course: Number(rnd(0, 360).toFixed(0)), - state: isFishing ? pick(["표류", "투망", "양망"]) : sp < 1 ? "정박" : "항해중", - zone, - isFishing, - owner, - region, - pairId: null, - pairDistNm: null, - nearVesselIds: [], - }; - - vessels.push(v); - if (attachToOwner) attachToOwner.vessels.push(v.id); - else owners.push({ name: owner, region, vessels: [v.id], type: "gn" }); - } - - // OT - for (let i = 0; i < VESSEL_TYPES.OT.count; i++) { - const owner = `${pick(SURNAMES)}${pick(SURNAMES)}远洋渔业`; - const region = pick(REGIONS); - const zone = pick(["2", "3"]); - const { lat, lon } = randomPointInZone(zone); - const isFishing = Math.random() < 0.5; - const sp = isFishing ? rnd(2.5, 5) : rnd(5, 10); - const id = vid++; - const v: Vessel = { - id, - permit: makePermit(id, "A"), - code: "OT", - color: VESSEL_TYPES.OT.color, - lat, - lon, - speed: Number(sp.toFixed(1)), - course: Number(rnd(0, 360).toFixed(0)), - state: isFishing ? "조업중" : "항해중", - zone, - isFishing, - owner, - region, - pairId: null, - pairDistNm: null, - nearVesselIds: [], - }; - vessels.push(v); - owners.push({ name: owner, region, vessels: [v.id], type: "ot" }); - } - - // PS - for (let i = 0; i < VESSEL_TYPES.PS.count; i++) { - const owner = `${pick(SURNAMES)}${pick(SURNAMES)}水产`; - const region = pick(REGIONS); - const zone = pick(["1", "2", "3", "4"]); - const { lat, lon } = randomPointInZone(zone); - const isFishing = Math.random() < 0.5; - const sp = isFishing ? rnd(0.3, 1.5) : rnd(5, 9); - const id = vid++; - const v: Vessel = { - id, - permit: makePermit(id, "A"), - code: "PS", - color: VESSEL_TYPES.PS.color, - lat, - lon, - speed: Number(sp.toFixed(1)), - course: Number(rnd(0, 360).toFixed(0)), - state: isFishing ? pick(["위망", "채낚기"]) : "항해중", - zone, - isFishing, - owner, - region, - pairId: null, - pairDistNm: null, - nearVesselIds: [], - }; - vessels.push(v); - owners.push({ name: owner, region, vessels: [v.id], type: "ps" }); - } - - // FC — assigned to trawl owners (positioned near PT) - const trawlOwners = owners.filter((o) => o.type === "trawl"); - for (let i = 0; i < VESSEL_TYPES.FC.count; i++) { - const oi = i < trawlOwners.length ? trawlOwners[i] : pick(trawlOwners); - - const refId = oi.vessels.find((id) => vessels[id - 1]?.code === "PT") ?? oi.vessels[0]; - const ref = vessels[refId - 1]; - - const zone = pick(["2", "3"]); - const lat = ref.lat + rnd(-0.2, 0.2); - const lon = ref.lon + rnd(-0.2, 0.2); - - const isNear = Math.random() < 0.4; - const sp = isNear ? rnd(0.5, 1.5) : rnd(5, 9); - - const nearVesselIds = isNear ? oi.vessels.filter((id) => vessels[id - 1]?.code !== "FC").slice(0, 2) : []; - - const v: Vessel = { - id: vid, - permit: makePermit(vid, "A"), - code: "FC", - color: VESSEL_TYPES.FC.color, - lat, - lon, - speed: Number(sp.toFixed(1)), - course: Number(rnd(0, 360).toFixed(0)), - state: isNear ? "환적" : "항해중", - zone, - isFishing: isNear, // kept from prototype: treat "환적" as fishing-like activity - owner: oi.name, - region: oi.region, - pairId: null, - pairDistNm: null, - nearVesselIds, - }; - vid += 1; - vessels.push(v); - oi.vessels.push(v.id); - } - - // Ensure initial pair distances are consistent with actual coordinates. - for (const p of ptPairs) { - const a = vessels[p.mainId - 1]; - const b = vessels[p.subId - 1]; - const d = haversineNm(a.lat, a.lon, b.lat, b.lon); - a.pairDistNm = d; - b.pairDistNm = d; - } - - return { vessels, owners, ptPairs }; -} - -export function tickMockFleetState(state: FleetState) { - for (const v of state.vessels) { - v.lat += (0.5 - Math.random()) * 0.003; - v.lon += (0.5 - Math.random()) * 0.003; - v.speed = Math.max(0, Number((v.speed + (0.5 - Math.random()) * 0.4).toFixed(1))); - v.course = Number(((v.course + (0.5 - Math.random()) * 6 + 360) % 360).toFixed(0)); - } - - for (const p of state.ptPairs) { - const a = state.vessels[p.mainId - 1]; - const b = state.vessels[p.subId - 1]; - const d = haversineNm(a.lat, a.lon, b.lat, b.lon); - a.pairDistNm = d; - b.pairDistNm = d; - } -} - -export function isVesselCode(code: string): code is VesselTypeCode { - return code === "PT" || code === "PT-S" || code === "GN" || code === "OT" || code === "PS" || code === "FC"; -} diff --git a/apps/web/src/shared/lib/color/hexToRgb.ts b/apps/web/src/shared/lib/color/hexToRgb.ts deleted file mode 100644 index 9c636ad..0000000 --- a/apps/web/src/shared/lib/color/hexToRgb.ts +++ /dev/null @@ -1,7 +0,0 @@ -export function hexToRgb(hex: string): [number, number, number] { - const m = /^#?([0-9a-fA-F]{6})$/.exec(hex.trim()); - if (!m) return [255, 255, 255]; - const n = parseInt(m[1], 16); - return [(n >> 16) & 255, (n >> 8) & 255, n & 255]; -} -