gc-wing/apps/web/src/widgets/map3d/lib/tooltips.ts

170 lines
5.6 KiB
TypeScript
Raw Normal View 히스토리

import type { AisTarget } from '../../../entities/aisTarget/model/types';
import type { LegacyVesselInfo } from '../../../entities/legacyVessel/model/types';
import { isFiniteNumber, toSafeNumber } from './setUtils';
export function formatNm(value: number | null | undefined) {
if (!isFiniteNumber(value)) return '-';
return `${value.toFixed(2)} NM`;
}
export function getLegacyTag(legacyHits: Map<number, LegacyVesselInfo> | null | undefined, mmsi: number) {
const legacy = legacyHits?.get(mmsi);
if (!legacy) return null;
return `${legacy.permitNo} (${legacy.shipCode})`;
}
export function getTargetName(
mmsi: number,
targetByMmsi: Map<number, AisTarget>,
legacyHits: Map<number, LegacyVesselInfo> | null | undefined,
) {
const legacy = legacyHits?.get(mmsi);
const target = targetByMmsi.get(mmsi);
return (
(target?.name || '').trim() || legacy?.shipNameCn || legacy?.shipNameRoman || `MMSI ${mmsi}`
);
}
export function getShipTooltipHtml({
mmsi,
targetByMmsi,
legacyHits,
}: {
mmsi: number;
targetByMmsi: Map<number, AisTarget>;
legacyHits: Map<number, LegacyVesselInfo> | null | undefined;
}) {
const legacy = legacyHits?.get(mmsi);
const t = targetByMmsi.get(mmsi);
const name = getTargetName(mmsi, targetByMmsi, legacyHits);
const sog = isFiniteNumber(t?.sog) ? t.sog : null;
const cog = isFiniteNumber(t?.cog) ? t.cog : null;
const msg = t?.messageTimestamp ?? null;
const vesselType = t?.vesselType || '';
const legacyHtml = legacy
? `<div style="margin-top: 6px; padding-top: 6px; border-top: 1px solid rgba(255,255,255,.08)">
<div><b>CN Permit</b> · <b>${legacy.shipCode}</b> · ${legacy.permitNo}</div>
<div>유효범위: ${legacy.workSeaArea || '-'}</div>
</div>`
: '';
return {
html: `<div style="font-family: system-ui; font-size: 12px; white-space: nowrap;">
<div style="font-weight: 700; margin-bottom: 4px;">${name}</div>
<div>MMSI: <b>${mmsi}</b>${vesselType ? ` · ${vesselType}` : ''}</div>
<div>SOG: <b>${sog ?? '?'}</b> kt · COG: <b>${cog ?? '?'}</b>°</div>
${msg ? `<div style="opacity:.7; font-size: 11px; margin-top: 4px;">${msg}</div>` : ''}
${legacyHtml}
</div>`,
};
}
export function getPairLinkTooltipHtml({
warn,
distanceNm,
aMmsi,
bMmsi,
legacyHits,
targetByMmsi,
}: {
warn: boolean;
distanceNm: number | null | undefined;
aMmsi: number;
bMmsi: number;
legacyHits: Map<number, LegacyVesselInfo> | null | undefined;
targetByMmsi: Map<number, AisTarget>;
}) {
const d = formatNm(distanceNm);
const a = getTargetName(aMmsi, targetByMmsi, legacyHits);
const b = getTargetName(bMmsi, targetByMmsi, legacyHits);
const aTag = getLegacyTag(legacyHits, aMmsi);
const bTag = getLegacyTag(legacyHits, bMmsi);
return {
html: `<div style="font-family: system-ui; font-size: 12px;">
<div style="font-weight: 700; margin-bottom: 4px;"> </div>
<div>${aTag ?? `MMSI ${aMmsi}`}</div>
<div style="opacity:.85;"> ${bTag ?? `MMSI ${bMmsi}`}</div>
<div style="margin-top: 4px;">: <b>${d}</b> · : <b>${warn ? '주의' : '정상'}</b></div>
<div style="opacity:.6; margin-top: 3px;">${a} / ${b}</div>
</div>`,
};
}
export function getFcLinkTooltipHtml({
suspicious,
distanceNm,
fcMmsi,
otherMmsi,
legacyHits,
targetByMmsi,
}: {
suspicious: boolean;
distanceNm: number | null | undefined;
fcMmsi: number;
otherMmsi: number;
legacyHits: Map<number, LegacyVesselInfo> | null | undefined;
targetByMmsi: Map<number, AisTarget>;
}) {
const d = formatNm(distanceNm);
const a = getTargetName(fcMmsi, targetByMmsi, legacyHits);
const b = getTargetName(otherMmsi, targetByMmsi, legacyHits);
const aTag = getLegacyTag(legacyHits, fcMmsi);
const bTag = getLegacyTag(legacyHits, otherMmsi);
return {
html: `<div style="font-family: system-ui; font-size: 12px;">
<div style="font-weight: 700; margin-bottom: 4px;"> </div>
<div>${aTag ?? `MMSI ${fcMmsi}`}</div>
<div style="opacity:.85;"> ${bTag ?? `MMSI ${otherMmsi}`}</div>
<div style="margin-top: 4px;">: <b>${d}</b> · : <b>${suspicious ? '의심' : '일반'}</b></div>
<div style="opacity:.6; margin-top: 3px;">${a} / ${b}</div>
</div>`,
};
}
export function getRangeTooltipHtml({
warn,
distanceNm,
aMmsi,
bMmsi,
legacyHits,
}: {
warn: boolean;
distanceNm: number | null | undefined;
aMmsi: number;
bMmsi: number;
legacyHits: Map<number, LegacyVesselInfo> | null | undefined;
}) {
const d = formatNm(distanceNm);
const aTag = getLegacyTag(legacyHits, aMmsi);
const bTag = getLegacyTag(legacyHits, bMmsi);
const radiusNm = toSafeNumber(distanceNm);
return {
html: `<div style="font-family: system-ui; font-size: 12px;">
<div style="font-weight: 700; margin-bottom: 4px;"> </div>
<div>${aTag ?? `MMSI ${aMmsi}`}</div>
<div style="opacity:.85;"> ${bTag ?? `MMSI ${bMmsi}`}</div>
<div style="margin-top: 4px;">: <b>${d}</b> · : <b>${formatNm(radiusNm == null ? null : radiusNm / 2)}</b> · : <b>${warn ? '주의' : '정상'}</b></div>
</div>`,
};
}
export function getFleetCircleTooltipHtml({
ownerKey,
ownerLabel,
count,
}: {
ownerKey: string;
ownerLabel?: string;
count: number;
}) {
const displayOwner = ownerLabel && ownerLabel.trim() ? ownerLabel : ownerKey;
return {
html: `<div style="font-family: system-ui; font-size: 12px;">
<div style="font-weight: 700; margin-bottom: 4px;"> </div>
<div>소유주: ${displayOwner || '-'}</div>
<div> : <b>${count}</b></div>
</div>`,
};
}