release: bump version to 0.47.0

This commit is contained in:
linkong
2026-04-30 16:56:37 +08:00
parent f22079d33a
commit 421234301a
42 changed files with 2501 additions and 454 deletions

View File

@@ -210,7 +210,7 @@ export const PATHS = {
export const VESSEL_CONFIG = {
altitudeOffset: 0.2,
maxRenderedMarkers: 5000,
maxRenderedMarkers: 0,
marker: {
baseScale: 7.5,
baseOpacity: 0.88,

View File

@@ -787,12 +787,13 @@ function formatVesselStatus(navStatus) {
function showVesselInfo(marker, coords) {
setLegendMode("vessels");
const vesselType = marker.userData?.vessel_type_display || marker.userData?.vessel_type_name || "-";
showInfoCard("vessel", {
name: marker.userData?.name || `MMSI ${marker.userData?.mmsi}`,
mmsi: marker.userData?.mmsi,
imo: marker.userData?.imo || "-",
flag: marker.userData?.flag || "-",
vessel_type: marker.userData?.vessel_type_name || "-",
vessel_type: vesselType,
speed: marker.userData?.sog ?? "-",
course: marker.userData?.cog ?? marker.userData?.heading ?? "-",
status: formatVesselStatus(marker.userData?.nav_status),
@@ -806,7 +807,8 @@ function showVesselInfo(marker, coords) {
function getVesselBriefHtml(marker) {
const name = marker.userData?.name || `MMSI ${marker.userData?.mmsi}`;
const speed = marker.userData?.sog ?? "-";
return `<strong>${name}</strong><br>${marker.userData?.vessel_type_name || "Vessel"} · ${speed} kn`;
const vesselType = marker.userData?.vessel_type_display || marker.userData?.vessel_type_name || "Vessel";
return `<strong>${name}</strong><br>${vesselType} · ${speed} kn`;
}
function getComputeCenterBriefHtml(marker) {
@@ -1390,6 +1392,7 @@ function resolveEarthSearchResults(query) {
marker.userData?.mmsi,
marker.userData?.imo,
marker.userData?.flag,
marker.userData?.vessel_type_display,
marker.userData?.vessel_type_name,
"船只 船舶 ais vessel ship maritime",
);
@@ -1401,7 +1404,7 @@ function resolveEarthSearchResults(query) {
typeLabel: "船只",
title: marker.userData?.name || `MMSI ${marker.userData?.mmsi}`,
subtitle: [
marker.userData?.vessel_type_name,
marker.userData?.vessel_type_display || marker.userData?.vessel_type_name,
marker.userData?.flag,
marker.userData?.sog !== undefined ? `${marker.userData.sog} kn` : null,
].filter(Boolean).join(" · ") || "AIS 船只",

View File

@@ -24,6 +24,17 @@ function normalizeVesselType(value, code) {
return "other";
}
function formatVesselTypeLabel(type, fallback = "") {
const rawFallback = String(fallback || "").trim();
const normalized = String(type || "").trim().toLowerCase();
if (normalized === "cargo") return "Cargo";
if (normalized === "tanker") return "Tanker";
if (normalized === "passenger") return "Passenger";
if (normalized === "fishing") return "Fishing";
if (normalized === "military") return "Military";
return rawFallback && rawFallback.toLowerCase() !== "other" ? rawFallback : "Other";
}
function drawVesselShape(context, anchored, glow, color = "#ffffff") {
context.fillStyle = color;
context.globalAlpha = anchored ? 0.55 : 0.96;
@@ -52,6 +63,7 @@ function buildVesselMarkerData(feature) {
if (!Number.isFinite(latitude) || !Number.isFinite(longitude)) return null;
const type = normalizeVesselType(props.vessel_type_name, props.vessel_type);
const vesselTypeLabel = formatVesselTypeLabel(type, props.vessel_type_name);
const navStatus = Number(props.nav_status);
const speed = Number(props.sog);
const anchored = navStatus === 1 || navStatus === 5 || (Number.isFinite(speed) && speed < 0.5);
@@ -61,6 +73,7 @@ function buildVesselMarkerData(feature) {
latitude,
longitude,
type,
vessel_type_display: vesselTypeLabel,
anchored,
course: Number(props.cog ?? props.heading ?? 0),
};
@@ -191,7 +204,10 @@ export function clearVesselData(earth) {
export async function loadVessels(_scene, earth, options = {}) {
const params = new URLSearchParams();
params.set("limit", String(options.limit || VESSEL_CONFIG.maxRenderedMarkers));
const requestedLimit = Number(options.limit ?? VESSEL_CONFIG.maxRenderedMarkers);
if (Number.isFinite(requestedLimit) && requestedLimit > 0) {
params.set("limit", String(requestedLimit));
}
const response = await fetch(`${PATHS.vesselsApi}?${params.toString()}`);
if (!response.ok) {
throw new Error(`Vessels HTTP ${response.status}`);
@@ -200,10 +216,12 @@ export async function loadVessels(_scene, earth, options = {}) {
const features = Array.isArray(payload?.features) ? payload.features : [];
clearVesselData(earth);
const markerData = features
let markerData = features
.map((feature) => buildVesselMarkerData(feature))
.filter(Boolean)
.slice(0, VESSEL_CONFIG.maxRenderedMarkers);
.filter(Boolean);
if (Number.isFinite(requestedLimit) && requestedLimit > 0) {
markerData = markerData.slice(0, requestedLimit);
}
vesselIconLayer.setData(markerData);
vesselIconLayer.attach(earth);