chore: 미사용 데드코드 삭제
- entities/vessel/lib/filter.ts (미사용 필터 유틸) - entities/vessel/model/mockFleet.ts (미사용 mock 데이터) - shared/lib/color/hexToRgb.ts (MapSettingsPanel 로컬 중복) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
부모
4d1a8a0d1e
커밋
c2ca830ef0
@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
@ -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<ZoneId, { lat: [number, number]; lon: [number, number] }> = {
|
||||
"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<T>(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<ZoneId>(["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<ZoneId>(["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<ZoneId>(["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<ZoneId>(["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<ZoneId>(["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";
|
||||
}
|
||||
@ -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];
|
||||
}
|
||||
|
||||
불러오는 중...
Reference in New Issue
Block a user