From fca188f0be9aa59518385d86d2201b9c54982db8 Mon Sep 17 00:00:00 2001 From: taDuc Date: Sun, 3 May 2026 01:04:50 +0700 Subject: [PATCH] scale label --- commit_snapshot.md | 41 ++- src/uhm/components/Map.tsx | 255 ++++++++---------- src/uhm/lib/editor/snapshot/editorSnapshot.ts | 15 +- src/uhm/types/sections.ts | 18 +- 4 files changed, 143 insertions(+), 186 deletions(-) diff --git a/commit_snapshot.md b/commit_snapshot.md index e997c35..70026bf 100644 --- a/commit_snapshot.md +++ b/commit_snapshot.md @@ -9,16 +9,13 @@ Nguồn tham chiếu trong code: - Type snapshot: `FrontEndAdmin/src/uhm/types/sections.ts` (`EditorSnapshot`) - Build snapshot khi commit: `FrontEndAdmin/src/uhm/lib/editor/snapshot/editorSnapshot.ts` (`buildEditorSnapshot`) -## 1) Schema tổng quan (v1) +## 1) Schema tổng quan (v2) -Hiện tại snapshot được ghi với `schema_version: 1`. +Hiện tại snapshot mới được ghi với `schema_version: 2` và **đã bỏ hẳn `section`** (vì flow BEGo đã có `commits.project_id`). ```ts -export type CommitSnapshotV1 = { - schema_version: 1; - - // Project/section đang được edit (FE vẫn giữ tên "section" cho compatibility) - section: { id: string; title: string }; +export type CommitSnapshotV2 = { + schema_version: 2; // GeoJSON draft để render map + làm nguồn dựng geometries/link_scopes editor_feature_collection?: FeatureCollection; @@ -67,9 +64,8 @@ Ngoài ra snapshot có `entity_wikis[]` để nối entity <-> wiki. ```mermaid classDiagram - class CommitSnapshotV1 { +class CommitSnapshotV2 { +number schema_version - +SectionRef section +FeatureCollection editor_feature_collection? +EntitySnapshot[] entities? +GeometrySnapshot[] geometries? @@ -78,10 +74,6 @@ classDiagram +EntityWikiLinkSnapshot[] entity_wikis? } - class SectionRef { - +string id - +string title - } class FeatureCollection { +string type // "FeatureCollection" @@ -174,25 +166,23 @@ classDiagram +string id } - CommitSnapshotV1 --> SectionRef - CommitSnapshotV1 --> FeatureCollection + CommitSnapshotV2 --> FeatureCollection FeatureCollection --> Feature Feature --> FeatureProperties - CommitSnapshotV1 --> EntitySnapshot - CommitSnapshotV1 --> GeometrySnapshot - CommitSnapshotV1 --> LinkScopeSnapshot - CommitSnapshotV1 --> WikiSnapshot - CommitSnapshotV1 --> EntityWikiLinkSnapshot + CommitSnapshotV2 --> EntitySnapshot + CommitSnapshotV2 --> GeometrySnapshot + CommitSnapshotV2 --> LinkScopeSnapshot + CommitSnapshotV2 --> WikiSnapshot + CommitSnapshotV2 --> EntityWikiLinkSnapshot ``` ## 4) Ý nghĩa từng phần -### 4.1 `section` +### 4.1 (Bỏ) `section` -Chỉ là “ref” tối thiểu để biết commit này thuộc project nào: +Từ `schema_version: 2`, snapshot **không còn** field `section`. -- `section.id` = `project_id` -- `section.title` = title tại thời điểm commit (phục vụ UI) +Nguồn chuẩn để biết commit thuộc project nào là `commits.project_id` (record/endpoint context), không phải snapshot. ### 4.2 `editor_feature_collection` @@ -305,8 +295,7 @@ Ví dụ dưới đây thể hiện: ```json { - "schema_version": 1, - "section": { "id": "019d...project", "title": "Project A" }, + "schema_version": 2, "editor_feature_collection": { "type": "FeatureCollection", "features": [ diff --git a/src/uhm/components/Map.tsx b/src/uhm/components/Map.tsx index abc0cba..8ea49c5 100644 --- a/src/uhm/components/Map.tsx +++ b/src/uhm/components/Map.tsx @@ -285,15 +285,15 @@ export default function Map({ "background-color": "#0b1220", }, }, - { - id: "graticules-line", - type: "line", - source: "base", - "source-layer": "graticules", - paint: { - "line-color": "#334155", - "line-width": [ - "interpolate", + { + id: "graticules-line", + type: "line", + source: "base", + "source-layer": "ne_10m_graticules_10", + paint: { + "line-color": "#334155", + "line-width": [ + "interpolate", ["linear"], ["zoom"], 0, 0.3, @@ -303,34 +303,34 @@ export default function Map({ "line-opacity": 0.55, }, }, - { - id: "land", - type: "fill", - source: "base", - "source-layer": "land", - paint: { - "fill-color": "#1e293b", - "fill-opacity": 0.25, - }, - }, - { - id: "bg-countries-fill", - type: "fill", - source: "base", - "source-layer": "countries", - paint: { - "fill-color": COUNTRY_FILL_COLOR_EXPRESSION, - "fill-opacity": 0.38, - }, - }, + { + id: "land", + type: "fill", + source: "base", + "source-layer": "ne_10m_land", + paint: { + "fill-color": "#1e293b", + "fill-opacity": 0.25, + }, + }, + { + id: "bg-countries-fill", + type: "fill", + source: "base", + "source-layer": "ne_10m_admin_0_countries", + paint: { + "fill-color": COUNTRY_FILL_COLOR_EXPRESSION, + "fill-opacity": 0.38, + }, + }, { - id: "bg-country-borders-line", - type: "line", - source: "base", - "source-layer": "country_borders", - paint: { - "line-color": "#cbd5e1", - "line-width": [ + id: "bg-country-borders-line", + type: "line", + source: "base", + "source-layer": "ne_10m_admin_0_boundary_lines_land", + paint: { + "line-color": "#cbd5e1", + "line-width": [ "interpolate", ["linear"], ["zoom"], @@ -341,87 +341,55 @@ export default function Map({ "line-opacity": 0.85, }, }, - { - id: "country-labels", - type: "symbol", - source: "base", - // New tiles build uses NaturalEarth label points layer name. - // If your tile pipeline exposes a different name, adjust here. - "source-layer": "ne_10m_admin_0_label_points", - minzoom: 0, - layout: { - "text-field": [ - "coalesce", - ["get", "sr_subunit"], - ["get", "NAME_EN"], - ["get", "NAME"], - ["get", "ADMIN"], - ["get", "name"], - "", - ], + { + id: "country-labels", + type: "symbol", + source: "base", + // A dedicated label-point layer (1 point per country) to avoid per-tile duplicates. + "source-layer": "country_labels", + minzoom: 0, + layout: { + "text-field": [ + "coalesce", + ["get", "NAME_EN"], + ["get", "NAME"], + ["get", "ADMIN"], + ["get", "name"], + "", + ], "text-size": [ "interpolate", ["linear"], ["zoom"], - 0, 10, - 3, 11, - 6, 14, + 0, 15, + 1, 16, + 2, 17, + 4, 19, + 6, 23, ], - "text-max-width": 10, - "text-allow-overlap": false, - "symbol-placement": "point", - }, - paint: { - "text-color": "#e2e8f0", - "text-halo-color": "#0b1220", - "text-halo-width": 1.2, - "text-halo-blur": 0.5, - }, - }, - // Fallback for tile pipelines that expose country labels under a generic "labels" layer. - // Hidden/shown together with "country-labels" toggle. - { - id: "country-labels-alt", - type: "symbol", - source: "base", - "source-layer": "labels", - minzoom: 0, - layout: { - "text-field": [ - "coalesce", - ["get", "name"], - ["get", "title"], - ["get", "NAME"], - "", - ], - "text-size": [ - "interpolate", - ["linear"], - ["zoom"], - 0, 10, - 3, 11, - 6, 14, - ], - "text-max-width": 10, - "text-allow-overlap": false, - "symbol-placement": "point", - }, - paint: { - "text-color": "#e2e8f0", - "text-halo-color": "#0b1220", - "text-halo-width": 1.2, - "text-halo-blur": 0.5, - }, - }, - { - id: "regions-line", - type: "line", - source: "base", - "source-layer": "regions", - paint: { - "line-color": "#475569", - "line-width": [ - "interpolate", + "text-padding": 0, + "text-max-width": 10, + // Prefer showing labels earlier (even if it means some collisions). + "text-allow-overlap": true, + "text-ignore-placement": true, + "symbol-placement": "point", + }, + paint: { + "text-color": "#e2e8f0", + "text-halo-color": "#0b1220", + "text-halo-width": 1.2, + "text-halo-blur": 0.5, + }, + }, + { + id: "regions-line", + type: "line", + source: "base", + "source-layer": "ne_10m_geography_regions_polys", + paint: { + "line-color": "#475569", + "line-width": [ + "interpolate", ["linear"], ["zoom"], 0, 0.2, @@ -431,25 +399,25 @@ export default function Map({ "line-opacity": 0.6, }, }, - { - id: "lakes-fill", - type: "fill", - source: "base", - "source-layer": "lakes", - paint: { - "fill-color": "#1d4ed8", - "fill-opacity": 0.45, - }, - }, - { - id: "rivers-line", - type: "line", - source: "base", - "source-layer": "rivers", - paint: { - "line-color": "#38bdf8", - "line-width": [ - "interpolate", + { + id: "lakes-fill", + type: "fill", + source: "base", + "source-layer": "ne_10m_lakes", + paint: { + "fill-color": "#1d4ed8", + "fill-opacity": 0.45, + }, + }, + { + id: "rivers-line", + type: "line", + source: "base", + "source-layer": "ne_10m_rivers_lake_centerlines", + paint: { + "line-color": "#38bdf8", + "line-width": [ + "interpolate", ["linear"], ["zoom"], 0, 0.25, @@ -459,14 +427,14 @@ export default function Map({ "line-opacity": 0.85, }, }, - { - id: "geolines-line", - type: "line", - source: "base", - "source-layer": "geolines", - paint: { - "line-color": "#94a3b8", - "line-width": 1.2, + { + id: "geolines-line", + type: "line", + source: "base", + "source-layer": "ne_10m_geographic_lines", + paint: { + "line-color": "#94a3b8", + "line-width": 1.2, "line-opacity": 0.8, }, }, @@ -1186,15 +1154,6 @@ function applyBackgroundLayerVisibility( visibility[layer.id] ? "visible" : "none" ); } - - // Keep fallback country label layer in sync with the primary toggle. - if (map.getLayer("country-labels-alt")) { - map.setLayoutProperty( - "country-labels-alt", - "visibility", - visibility["country-labels"] ? "visible" : "none" - ); - } } function syncRasterBaseVisibility(map: maplibregl.Map, shouldShow: boolean) { diff --git a/src/uhm/lib/editor/snapshot/editorSnapshot.ts b/src/uhm/lib/editor/snapshot/editorSnapshot.ts index 7e5f7bf..353e214 100644 --- a/src/uhm/lib/editor/snapshot/editorSnapshot.ts +++ b/src/uhm/lib/editor/snapshot/editorSnapshot.ts @@ -10,11 +10,10 @@ import type { EntityWikiLinkSnapshot } from "@/uhm/types/sections"; export function normalizeEditorSnapshot(raw: unknown): EditorSnapshot | null { if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null; const snapshot = raw as EditorSnapshot; - if ( - snapshot.editor_feature_collection && - snapshot.editor_feature_collection.type === "FeatureCollection" && - Array.isArray(snapshot.editor_feature_collection.features) - ) { + // Accept legacy snapshots (v1) and new ones (v2+). We only require that a FeatureCollection, + // if present, is structurally valid. Everything else is treated as optional. + const fc = (snapshot as any).editor_feature_collection as FeatureCollection | undefined; + if (fc && fc.type === "FeatureCollection" && Array.isArray(fc.features)) { return snapshot; } return { @@ -225,11 +224,7 @@ export function buildEditorSnapshot(options: { }); return { - schema_version: 1, - section: { - id: options.section.id, - title: options.section.title, - }, + schema_version: 2, editor_feature_collection: JSON.parse(JSON.stringify(options.draft)) as FeatureCollection, entities: Array.from(entityRows.values()).map((entity) => { const id = String(entity.id || ""); diff --git a/src/uhm/types/sections.ts b/src/uhm/types/sections.ts index a45b0b4..34b44b6 100644 --- a/src/uhm/types/sections.ts +++ b/src/uhm/types/sections.ts @@ -60,8 +60,10 @@ export type SectionSubmission = { content?: string | null; }; -export type EditorSnapshot = { - schema_version: number; +export type EditorSnapshotV1 = { + schema_version: 1; + // Legacy: before BEGo flow moved fully to project/commit records, FE stored a minimal "section" ref + // inside snapshot_json. New snapshots omit this entirely. section: { id: string; title: string; @@ -74,6 +76,18 @@ export type EditorSnapshot = { entity_wikis?: EntityWikiLinkSnapshot[]; }; +export type EditorSnapshotV2 = { + schema_version: 2; + editor_feature_collection?: FeatureCollection; + entities?: EntitySnapshot[]; + geometries?: GeometrySnapshot[]; + link_scopes?: LinkScopeSnapshot[]; + wikis?: WikiSnapshot[]; + entity_wikis?: EntityWikiLinkSnapshot[]; +}; + +export type EditorSnapshot = EditorSnapshotV1 | EditorSnapshotV2; + export type EditorLoadResponse = { section: Section; state: SectionState;