release: bump version to 0.70.0
Some checks failed
ci / backend (push) Has been cancelled
ci / frontend (push) Has been cancelled
release / images (push) Has been cancelled
ci / delivery (push) Has been cancelled

This commit is contained in:
linkong
2026-06-04 17:16:23 +08:00
parent acbbfdf9e2
commit 8c204717cd
78 changed files with 1762 additions and 703 deletions

View File

@@ -38,6 +38,7 @@ This is the current Intelligent Planet documentation entry point. Docs are organ
- [Datasource Collector Settings and Connectivity](/home/ray/dev/linkong/planet/docs/technical/en/datasource-collector-settings-connectivity.md): datasource catalog, collector settings, connectivity validation, and credentials
- [Datasource API Performance](/home/ray/dev/linkong/planet/docs/technical/en/backend-datasources-api-performance.md): DataSources list API performance and caching
- [Data Jobs and Outbox Architecture](/home/ray/dev/linkong/planet/docs/technical/en/data-job-earth-sync-architecture.md): PostgreSQL job queue, outbox, listener, and Kafka / Spark evolution boundaries
- [Backend Enum and String Compatibility Contract](/home/ray/dev/linkong/planet/docs/technical/en/backend-enum-contracts.md): stable protocol states, historical string compatibility, and Earth news decision boundaries
- [Shared Location Resolution Pipeline Development Guide](/home/ray/dev/linkong/planet/docs/technical/en/location-pipeline-development.md): location resolver / pipeline interfaces, registries, and extension points
- [News Live Streams Collector Format](/home/ray/dev/linkong/planet/docs/technical/en/earth-news-live-streams-collector-format.md): news, live stream, and media collection payload conventions
- [Docs Gatekeeper Development Guide](/home/ray/dev/linkong/planet/docs/technical/en/docs-gatekeeper-development.md): backend Docs catalog, Markdown content loading, and Gatekeeper permission groups

View File

@@ -93,7 +93,9 @@ The CelesTrak TLE collector prefers the complete `active` catalog. If CelesTrak
| BarentsWatch AIS | vessel | AIS vessel positions, speed, heading, MMSI, and related fields | Collector settings |
| AISStream Vessels | vessel_ais | AIS WebSocket realtime stream, written to the raw observation layer and displayed through aggregation | Collector settings |
AIS vessel collectors use a different persistence path from regular `CollectedData` collectors. BarentsWatch, AISStream, and custom `vessel_ais` sources write into the AIS raw observation layer first, then the aggregation service merges those observations into the GeoJSON and detail payloads used by the Earth vessel layer. This preserves source, transport, field conflicts, and observation time instead of letting one realtime source overwrite the final display table.
AIS vessel collectors use a different persistence path from regular `CollectedData` collectors. BarentsWatch, AISStream, and custom `vessel_ais` sources write into the AIS raw observation layer and also upsert `vessel_current_state`: one row per MMSI with the latest position, speed, course, navigation state, vessel type, name, and source metadata. This preserves raw observation history for tracks, audit, and situational analysis while letting the Earth vessel layer read the current-state table instead of scanning historical AIS rows.
`vessel_current_state` only lets newer observations overwrite dynamic position fields; static fields such as name and vessel type are merged by non-empty value and source priority. Earth snapshots return only vessels inside the freshness window, keeping high-frequency AIS history out of the display path.
Earth boundaries are no longer data collectors. They are Earth static rendering assets: the Earth Assets settings panel owns source configuration, and `/api/v1/earth/boundaries/*` builds `frontend/public/earth/data/boundaries/earth-boundaries-china-pov-v1.pmtiles`. When no high-precision PMTiles artifact is available locally, the frontend uses the bundled low-precision GeoJSON fallback and does not write boundary records to `CollectedData`.
@@ -339,32 +341,18 @@ AIS observations do not directly replace final vessel records. They are first sa
- Dynamic fields such as position, speed, and course are selected by freshness and source priority.
- Static fields prefer non-empty values; conflicting candidates are recorded for detail and diagnostics views.
Earth vessel rendering now consumes the bounded snapshot endpoint and realtime delta channel:
Earth vessel rendering now consumes the current-state snapshot endpoint:
```http
GET /api/v1/vessels/snapshot?bbox=lon_min,lat_min,lon_max,lat_max&zoom=12&limit=1000
GET /api/v1/vessels/snapshot?bbox=-180,-85.05112878,180,85.05112878&zoom=12&limit=3000
GET /api/v1/visualization/vessels/{mmsi}
GET /api/v1/visualization/vessels/{mmsi}/track
GET /api/v1/visualization/vessels/{mmsi}/conflicts
```
`/api/v1/vessels/snapshot` requires `bbox` and `zoom`, defaults to `limit=1000`, and caps `limit` at `5000`. It prefers aggregated `ais_raw_observations`; when the current raw window is empty, it can fall back to the latest legacy `vessel_position` / `vessel_static` rows and marks that path with `diagnostics.legacy_fallback_used`. The old `/api/v1/visualization/geo/vessels` route has been removed.
`/api/v1/vessels/snapshot` requires `bbox` and `zoom`, and caps `limit` at `5000`. The Earth frontend uses a global bbox for current state and does not refetch on camera viewport changes. The endpoint reads `vessel_current_state` and reports `diagnostics.source = "vessel_current_state"`. The old `/api/v1/visualization/geo/vessels` route has been removed.
Realtime deltas are sent through the `/ws` `vessels` channel. Clients must subscribe with the current viewport:
```json
{
"type": "subscribe",
"data": {
"channel": "vessels",
"bbox": [120.8, 30.7, 122.1, 31.8],
"zoom": 12,
"limit": 1000
}
}
```
The backend stores lightweight subscription filters per connection and only sends vessel updates that match the subscriber bbox. Collector broadcasts enter a 1-second throttle queue; within each flush window, only the latest update per MMSI is retained.
High-frequency AIS updates must not become per-delta full-layer rebuilds. If Earth uses the `/ws` `vessels` channel, it should send low-frequency reload/dirty hints and let the frontend merge snapshot refreshes. Tracks and conflicts still read historical facts through the single-vessel APIs.
### Layer APIs And Global Stats

View File

@@ -0,0 +1,50 @@
# Backend Enum and String Compatibility Contract
Planet keeps finite, stable protocol states in `backend/app/core/enums.py`. Databases and APIs continue to store and return lowercase strings. Enums provide internal type safety, validation, and deduplication without requiring a migration to database enum types.
## Selection Rules
A value should become an enum only when it is finite and stable, invalid outside the known set, and compared, sorted, or branched on by multiple modules. Typical examples are job states, AI Playground message roles and states, user roles, alert states, log levels, news importance, and Breaking states.
The following must remain configurable strings:
- news categories and tags;
- provider, model, datasource, collector, news source, and Feed identifiers;
- extensible incident and anomaly types; and
- user-authored and free-form text.
## Boundary Conversion
Services should use `StrEnum` internally. Database writes and external responses use `.value`, preserving existing strings such as `JobStatus.RUNNING.value == "running"`.
Use the compatibility parser when reading historical database values, JSON, or external input:
```python
status = parse_enum(JobStatus, raw_status, JobStatus.FAILED)
```
Known historical strings normalize to enum members. Empty values use the explicit default. Unknown values emit a warning and safely fall back instead of breaking historical reads. Pydantic request fields may use enums directly so invalid protocol values return `422`. Ordinary String and JSON database columns must not be converted to SQLAlchemy Enum.
## Earth News Decisions
Earth news decisions live in `backend/app/services/earth_news_classification.py`:
- category answers what the story is and remains configurable;
- importance answers whether it has long-term value; and
- Breaking answers whether it must temporarily jump ahead.
Importance levels are fixed:
| Level | Score |
|---|---:|
| `low` | 034 |
| `medium` | 3559 |
| `high` | 6079 |
| `critical` | 80100 |
Breaking rules use the typed `BreakingRule` structure. Levels, scopes, sources, and TTLs are managed by the public classification module. `earth_news.py` remains responsible for fetching, parsing, orchestration, and serialization.
## Regression Prevention
When adding or changing protocol states, check `app/core/enums.py` first and do not redefine duplicate `Literal` aliases, status sets, or normalization helpers in business modules. Add enum value and boundary contracts to `tests/test_enum_contracts.py`, and preserve API strings and database representation.

View File

@@ -318,10 +318,10 @@ Connectivity validation and actual collection are separate actions. A banner suc
The new vessel list entry point is no longer the legacy `/api/v1/visualization/geo/vessels` route. Earth initial state should call:
```http
GET /api/v1/vessels/snapshot?bbox=lon_min,lat_min,lon_max,lat_max&zoom=12&limit=1000
GET /api/v1/vessels/snapshot?bbox=-180,-85.05112878,180,85.05112878&zoom=12&limit=3000
```
That endpoint prefers local aggregated `ais_raw_observations`; when the current raw window is empty, it can fall back to the latest legacy `vessel_position` / `vessel_static` rows and exposes that through `diagnostics.legacy_fallback_used`. Realtime updates use the `/ws` `vessels` channel; subscriptions must include `bbox`, `zoom`, and `limit`. The server filters updates per connection and merges collector broadcasts every second, keeping only the latest position per MMSI.
That endpoint reads `vessel_current_state`, returning the latest point per MMSI inside the freshness window. Raw `ais_raw_observations` remain available for tracks, audit, and situational analysis, but the display endpoint no longer scans and aggregates history on the fly. Earth sends a global bbox rather than the current camera viewport. If the `/ws` `vessels` channel is connected, it should act as a reload/dirty hint for merged refreshes, not as a per-AIS-delta full-layer rebuild path.
## Custom REST / WebSocket Mapping Runtime

View File

@@ -179,9 +179,15 @@ The `earth:compute-center-location-saved` reconciliation pipeline is deliberatel
### AIS Vessel Layer
The vessel layer now uses `/api/v1/vessels/snapshot` for the initial viewport snapshot and the `/ws` `vessels` channel for realtime deltas. Snapshot requests must include `bbox`, `zoom`, and a bounded `limit`; the backend defaults to `limit=1000` and caps it at `5000`. WebSocket subscriptions must include the same viewport fields so the server can filter updates per connection.
The vessel layer uses `/api/v1/vessels/snapshot` for a global current-state snapshot. Earth sends the global bbox `[-180,-85.05112878,180,85.05112878]`, the current `zoom`, and `limit=3000` when the layer opens. It does not repeatedly refetch by camera viewport, and globe rotation or zoom does not reconnect a vessel WebSocket subscription.
The legacy `/api/v1/visualization/geo/vessels` route has been removed. Frontend code should fetch a snapshot for the current viewport when the layer opens, then subscribe to `vessels` deltas. After map pan or zoom, reload the snapshot and send a fresh vessels subscription. The backend only falls back to legacy `vessel_position` / `vessel_static` rows when the current raw window is empty; frontend code can detect that state through `diagnostics.legacy_fallback_used`.
The backend snapshot endpoint still requires `bbox`, but the Earth runtime treats it as the global current-state entry point. It reads `vessel_current_state`, returning one latest point per MMSI within the freshness window, and no longer scans or aggregates historical `ais_raw_observations` for the display layer. Raw AIS history remains available for tracks, audit, and situational analysis.
Vessel markers are rendered through `createInteractableLayer()` as batched `THREE.Points`, with `cluster` and `avoidance` explicitly disabled. Dense waterways may overlap. Dragging or inertial rotation skips hover picking; normal hover uses screen-space nearest-point picking. Do not reconnect vessels to dynamic screen clustering or per-frame Points rebuilds, because those interaction costs are what make a 3000-marker layer feel heavy.
If the `/ws` `vessels` channel is used by Earth, it should be a low-frequency reload/dirty hint only. Do not create multiple viewport subscriptions, and do not turn every AIS delta into a full layer rebuild.
The legacy `/api/v1/visualization/geo/vessels` route has been removed. Frontend code should keep using `PATHS.vesselsApi` and can verify the current-state path through `diagnostics.source == "vessel_current_state"`.
The new layer API family is `/api/v1/layers/*`, which separates map rendering payloads from aggregate panel statistics. Layer requests must include `bbox`, `zoom`, and a bounded `limit`; responses include `visible_count`, `returned_count`, and `diagnostics`, where `degraded`, `truncated`, and `limit_clamped` are the frontend signals for fallback UI. Right-side aggregate panels should not sum the layer response. They should read `/api/v1/data-products` or `/api/v1/data-products/{product_id}/status`, because those statistics stay global and do not change with the viewport.

View File

@@ -193,13 +193,13 @@ The land/ocean base is an Earth base-map asset and preloads at startup; the "Bor
| Vessel renderOrder | local `VESSEL_RENDER_ORDER` | `4.4` | Normal marker and interactive overlay |
| Vessel track renderOrder | `VESSEL_RENDER_ORDER - 0.1` | `4.3` | Below vessel markers |
| Vessel point pixel size | local `VESSEL_POINT_SIZE` | `34` | Shared size for normal markers and hover / locked overlays |
| Default vessel render cap | `VESSEL_CONFIG.maxRenderedMarkers` | `0` | `0` means the frontend does not clip by default; positive values send `limit` and clip markers |
| Default vessel render cap | `VESSEL_CONFIG.maxRenderedMarkers` | `3000` | Default `limit` sent to `/api/v1/vessels/snapshot` for the global current-state snapshot; the backend cap remains `5000` |
| Vessel texture canvas size | local `VESSEL_ATLAS_CELL_SIZE` | `128` | Canvas point texture |
| Course bucket count | local `VESSEL_COURSE_BINS` | `32` | Moving vessels are bucketed by COG to reduce draw calls while preserving direction |
| Vessel hover picking throttle | local `VESSEL_HOVER_PICK_INTERVAL_MS` | `100` | `main.js` hover picking |
| Vessel screen hit radius | local `VESSEL_POINTER_RADIUS_PX` | `22` | `main.js` screen-space picking |
AIS vessel markers use batched `THREE.Points`, not one `THREE.Sprite` per vessel. Moving vessels stay triangular, anchored or slow vessels stay circular, and hover / locked states add a same-size glow overlay. Vessel type color and info-card type text must come from the same normalized result: `vessels.js` reads both backend `vessel_type_name` and AIS numeric `vessel_type`, derives the color-driving `type`, then exposes `vessel_type_display` for the info card, hover summary, and search results.
AIS vessel markers use batched `THREE.Points`, not one `THREE.Sprite` per vessel. Moving vessels stay triangular, anchored or slow vessels stay circular, and hover / locked states add a same-size glow overlay. Vessels explicitly disable `cluster` and `avoidance`; dense areas may overlap and must not be reattached to dynamic screen clustering. Vessel type color and info-card type text must come from the same normalized result: `vessels.js` reads both backend `vessel_type_name` and AIS numeric `vessel_type`, derives the color-driving `type`, then exposes `vessel_type_display` for the info card, hover summary, and search results.
## Compute Centers

View File

@@ -80,6 +80,8 @@ Each item includes:
Official data, e-commerce metrics, major platforms, and numeric business signals increase importance. Press releases start with a lower baseline and rise only when they match stronger platform, amount, M&A, or regulatory signals.
Importance levels are fixed: `low` 034, `medium` 3559, `high` 6079, and `critical` 80100. Category, importance, and Breaking decisions are centralized in `earth_news_classification.py`. Category keys and tags remain configurable, while importance and Breaking protocol states use shared enums. Databases and APIs continue to store compatible lowercase strings.
## Configuration and Cache
`GET /api/v1/earth/news-sources` returns the default or saved configuration. `PUT /api/v1/earth/news-sources` saves it, increments `cache_version`, and clears the process region cache. `POST /api/v1/earth/news-sources/reset` restores defaults. `POST /api/v1/earth/news-sources/test` tests one RSS/Atom/Aggregated source without writing news items.
@@ -107,6 +109,27 @@ The Web Earth category chips only store the current browser preference; changing
Source testing only proves that a specific RSS/Atom/XML feed can be parsed. It does not mean those items have already been written to the news table or are visible in the current region/category view. Saving or resetting news sources increments the configuration version and clears cache; if an enabled feed has no recent stored items, the next `earth-feed` request supplements from RSS so newly enabled sources such as 36Kr and Ebrun are not masked by fresh Google News rows.
## Breaking News Insertion
The news system keeps three separate decisions:
- **Category**: what the story is about, such as business, military, or disaster.
- **Importance**: whether the story has long-term value, stored as `importance_score / importance_level`.
- **Breaking**: whether the story must temporarily jump ahead, stored in `location_meta.news_meta.breaking_*`.
Breaking metadata does not add physical columns. It remains in `location_meta.news_meta`:
- `breaking_level`: `none / watch / breaking / critical`.
- `breaking_scope`: `regional / global`.
- `breaking_reasons`: rule or operator reasons.
- `breaking_source`: `rules / ai / manual / multi_source`.
- `breaking_confidence`: 0 to 1.
- `breaking_expires_at`: expiration timestamp.
The backend performs the ordering, so Web and UE clients do not need to reorder items. Active `critical`, `breaking`, and `watch` items appear before normal items in that order. Once expired, the item falls back to normal ordering without being deleted or changing its long-term importance score.
`breaking_scope = global` bypasses the active region and can appear in every regional feed. `regional` breaking follows the normal active-region-plus-global rule. The response `filters` includes `has_breaking` and `highest_breaking_level`, which the Earth client uses to apply restrained panel and card styling.
## Connectivity Monitoring
`POST /api/v1/earth/news-sources/test` tests one source and writes the result to `earth_news_sources.health[source_id]`. Normal RSS/Atom fetches update the same health map.

View File

@@ -27,8 +27,10 @@ Note: the layer control panel order and the registration / startup load order ar
| 2.29 | Country border hover glow | `country-boundaries.js` | Hover radius + glow offset | `depthTest: false`, raycast disabled | Additive glow to reinforce border edge and terrain hover visibility. |
| 2.3 | Country border hover line | `country-boundaries.js` | `hoverAltitudeOffset = EARTH_SURFACE_TEXTURE_ALTITUDE_OFFSET = 0.48` | `depthTest: false`, raycast disabled | Neon red-orange hover line; aligned with the normal borders and HD texture shell to avoid ghosting or floating; China and Taiwan share the same highlight group. |
| 3 | Satellite footprint fill / Iridium coverage ring | `satellites.js`, `iridium-footprint-adapter.js` | `GROUND_FOOTPRINT_RENDER_ORDER` | depth-tested; Iridium adapter fill / ring use the same renderOrder | Footprint above land / texture / terrain and country borders, below compute centers and satellites. |
| 3-5 | BGP markers and overlays | `bgp.js` | Each marker's own renderOrder | BGP picking path | Preserves existing BGP visual level. |
| 4.5 | Compute centers | `compute-centers.js` | `COMPUTE_CENTER_RENDER_ORDER` | Compute center picking path | Surface facilities, below satellites. |
| 3-4.5 | BGP collectors, event rings, and event markers | `bgp.js`, `interactable.js` | Collector markers use `BGP_COLLECTOR_RENDER_ORDER = 4.4`; event markers use `BGP_EVENT_RENDER_ORDER = 4.5`; overlays remain in BGP-owned groups | Screen-space Interactable picking; BGP collectors/events may use collision avoidance where appropriate | BGP collectors share the surface-facility band with vessels; BGP events share the compute-center band. |
| 4.3 | AIS vessel track lines | `vessels.js` | `VESSEL_RENDER_ORDER - 0.1`; `CONFIG.earthRadius + VESSEL_CONFIG.track.altitudeOffset` | Follows vessel selection, not independently picked | Recent track for the selected vessel, below vessel markers. |
| 4.4 | AIS vessel markers | `vessels.js`, `interactable.js` | `VESSEL_RENDER_ORDER`; business radius `CONFIG.earthRadius + VESSEL_CONFIG.altitudeOffset`; normal markers are bucketed `THREE.Points`, hover / locked states are single-point overlays | `depthTest: true`; `main.js` uses screen-space picking for front-facing markers; `cluster: false` and `avoidance: false` | Moving vessels use triangular point textures, anchored / slow vessels use dots. Dense waterways may overlap and do not participate in dynamic screen clustering, avoiding Points rebuilds during rotation. |
| 4.5 | Compute centers | `compute-centers.js`, `interactable.js` | `COMPUTE_CENTER_RENDER_ORDER` | Screen-space Interactable picking, with surface-icon collision handling | Surface facilities, below satellites. |
| 5 | Satellite background dot | `satellites.js` | Fixed renderOrder; by default TLE/SGP4 altitude is compressed to `CONFIG.earthRadius + 4..25`; with real altitude disabled or propagation failed, uses `fallbackAltitudeOffset = 8` | Screen-space satellite picking | Below satellite dots. |
| 6 | Satellite dots | `satellites.js` | Same compressed / fallback height as satellite backdrop dots | Screen-space satellite picking | Satellite dots above footprints and compute centers. |
| 12+ | Satellite locked ring, halo, predicted orbit | `satellites.js` | `SATELLITE_CONFIG.overlayRenderOrder` and offsets; predicted orbit follows the same real-altitude toggle and fixes the lock-time globe pose to draw a closed inertial orbit; returns to same-sphere mode when real altitude is disabled | Satellite overlay path | Used for selected/locked satellite emphasis. |

View File

@@ -118,12 +118,12 @@ sequenceDiagram
## Vessels
Vessel data shows AIS vessels, navigation state, vessel-type legend, and source health. Earth rendering uses position snapshots and static vessel information; it should not render every raw AIS observation.
Vessel data shows AIS vessels, navigation state, vessel-type legend, and source health. Earth rendering uses the `vessel_current_state` current-state snapshot; it should not render every raw AIS observation.
- **Collection entry**: AIS sources, BarentsWatch vessels.
- **Fact table**: `collected_data` or AIS raw observation tables.
- **Derived tables**: `vessel_static`, `vessel_position`, `ais_raw_observations`, `ais_source_health`.
- **API**: vessel visualization API returns current vessel markers and detail fields.
- **Derived tables**: `vessel_current_state`, `vessel_static`, `vessel_position`, `ais_raw_observations`, `ais_source_health`.
- **API**: `/api/v1/vessels/snapshot` returns current vessel markers and detail fields; the Earth frontend uses a global bbox for the global current-state view.
- **Delete semantics**: deleting any vessel source lets owned projections broadcast `vessels` with `clear_then_reload`.
- **Common failure**: the count panel changes but vessels remain. Summary and layer data are separate; Earth should clear objects based on layer updates.