scale label
This commit is contained in:
@@ -9,16 +9,13 @@ Nguồn tham chiếu trong code:
|
|||||||
- Type snapshot: `FrontEndAdmin/src/uhm/types/sections.ts` (`EditorSnapshot`)
|
- Type snapshot: `FrontEndAdmin/src/uhm/types/sections.ts` (`EditorSnapshot`)
|
||||||
- Build snapshot khi commit: `FrontEndAdmin/src/uhm/lib/editor/snapshot/editorSnapshot.ts` (`buildEditorSnapshot`)
|
- 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
|
```ts
|
||||||
export type CommitSnapshotV1 = {
|
export type CommitSnapshotV2 = {
|
||||||
schema_version: 1;
|
schema_version: 2;
|
||||||
|
|
||||||
// Project/section đang được edit (FE vẫn giữ tên "section" cho compatibility)
|
|
||||||
section: { id: string; title: string };
|
|
||||||
|
|
||||||
// GeoJSON draft để render map + làm nguồn dựng geometries/link_scopes
|
// GeoJSON draft để render map + làm nguồn dựng geometries/link_scopes
|
||||||
editor_feature_collection?: FeatureCollection;
|
editor_feature_collection?: FeatureCollection;
|
||||||
@@ -67,9 +64,8 @@ Ngoài ra snapshot có `entity_wikis[]` để nối entity <-> wiki.
|
|||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
classDiagram
|
classDiagram
|
||||||
class CommitSnapshotV1 {
|
class CommitSnapshotV2 {
|
||||||
+number schema_version
|
+number schema_version
|
||||||
+SectionRef section
|
|
||||||
+FeatureCollection editor_feature_collection?
|
+FeatureCollection editor_feature_collection?
|
||||||
+EntitySnapshot[] entities?
|
+EntitySnapshot[] entities?
|
||||||
+GeometrySnapshot[] geometries?
|
+GeometrySnapshot[] geometries?
|
||||||
@@ -78,10 +74,6 @@ classDiagram
|
|||||||
+EntityWikiLinkSnapshot[] entity_wikis?
|
+EntityWikiLinkSnapshot[] entity_wikis?
|
||||||
}
|
}
|
||||||
|
|
||||||
class SectionRef {
|
|
||||||
+string id
|
|
||||||
+string title
|
|
||||||
}
|
|
||||||
|
|
||||||
class FeatureCollection {
|
class FeatureCollection {
|
||||||
+string type // "FeatureCollection"
|
+string type // "FeatureCollection"
|
||||||
@@ -174,25 +166,23 @@ classDiagram
|
|||||||
+string id
|
+string id
|
||||||
}
|
}
|
||||||
|
|
||||||
CommitSnapshotV1 --> SectionRef
|
CommitSnapshotV2 --> FeatureCollection
|
||||||
CommitSnapshotV1 --> FeatureCollection
|
|
||||||
FeatureCollection --> Feature
|
FeatureCollection --> Feature
|
||||||
Feature --> FeatureProperties
|
Feature --> FeatureProperties
|
||||||
CommitSnapshotV1 --> EntitySnapshot
|
CommitSnapshotV2 --> EntitySnapshot
|
||||||
CommitSnapshotV1 --> GeometrySnapshot
|
CommitSnapshotV2 --> GeometrySnapshot
|
||||||
CommitSnapshotV1 --> LinkScopeSnapshot
|
CommitSnapshotV2 --> LinkScopeSnapshot
|
||||||
CommitSnapshotV1 --> WikiSnapshot
|
CommitSnapshotV2 --> WikiSnapshot
|
||||||
CommitSnapshotV1 --> EntityWikiLinkSnapshot
|
CommitSnapshotV2 --> EntityWikiLinkSnapshot
|
||||||
```
|
```
|
||||||
|
|
||||||
## 4) Ý nghĩa từng phần
|
## 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`
|
Nguồn chuẩn để biết commit thuộc project nào là `commits.project_id` (record/endpoint context), không phải snapshot.
|
||||||
- `section.title` = title tại thời điểm commit (phục vụ UI)
|
|
||||||
|
|
||||||
### 4.2 `editor_feature_collection`
|
### 4.2 `editor_feature_collection`
|
||||||
|
|
||||||
@@ -305,8 +295,7 @@ Ví dụ dưới đây thể hiện:
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"schema_version": 1,
|
"schema_version": 2,
|
||||||
"section": { "id": "019d...project", "title": "Project A" },
|
|
||||||
"editor_feature_collection": {
|
"editor_feature_collection": {
|
||||||
"type": "FeatureCollection",
|
"type": "FeatureCollection",
|
||||||
"features": [
|
"features": [
|
||||||
|
|||||||
@@ -285,15 +285,15 @@ export default function Map({
|
|||||||
"background-color": "#0b1220",
|
"background-color": "#0b1220",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "graticules-line",
|
id: "graticules-line",
|
||||||
type: "line",
|
type: "line",
|
||||||
source: "base",
|
source: "base",
|
||||||
"source-layer": "graticules",
|
"source-layer": "ne_10m_graticules_10",
|
||||||
paint: {
|
paint: {
|
||||||
"line-color": "#334155",
|
"line-color": "#334155",
|
||||||
"line-width": [
|
"line-width": [
|
||||||
"interpolate",
|
"interpolate",
|
||||||
["linear"],
|
["linear"],
|
||||||
["zoom"],
|
["zoom"],
|
||||||
0, 0.3,
|
0, 0.3,
|
||||||
@@ -303,34 +303,34 @@ export default function Map({
|
|||||||
"line-opacity": 0.55,
|
"line-opacity": 0.55,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "land",
|
id: "land",
|
||||||
type: "fill",
|
type: "fill",
|
||||||
source: "base",
|
source: "base",
|
||||||
"source-layer": "land",
|
"source-layer": "ne_10m_land",
|
||||||
paint: {
|
paint: {
|
||||||
"fill-color": "#1e293b",
|
"fill-color": "#1e293b",
|
||||||
"fill-opacity": 0.25,
|
"fill-opacity": 0.25,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "bg-countries-fill",
|
id: "bg-countries-fill",
|
||||||
type: "fill",
|
type: "fill",
|
||||||
source: "base",
|
source: "base",
|
||||||
"source-layer": "countries",
|
"source-layer": "ne_10m_admin_0_countries",
|
||||||
paint: {
|
paint: {
|
||||||
"fill-color": COUNTRY_FILL_COLOR_EXPRESSION,
|
"fill-color": COUNTRY_FILL_COLOR_EXPRESSION,
|
||||||
"fill-opacity": 0.38,
|
"fill-opacity": 0.38,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "bg-country-borders-line",
|
id: "bg-country-borders-line",
|
||||||
type: "line",
|
type: "line",
|
||||||
source: "base",
|
source: "base",
|
||||||
"source-layer": "country_borders",
|
"source-layer": "ne_10m_admin_0_boundary_lines_land",
|
||||||
paint: {
|
paint: {
|
||||||
"line-color": "#cbd5e1",
|
"line-color": "#cbd5e1",
|
||||||
"line-width": [
|
"line-width": [
|
||||||
"interpolate",
|
"interpolate",
|
||||||
["linear"],
|
["linear"],
|
||||||
["zoom"],
|
["zoom"],
|
||||||
@@ -341,87 +341,55 @@ export default function Map({
|
|||||||
"line-opacity": 0.85,
|
"line-opacity": 0.85,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "country-labels",
|
id: "country-labels",
|
||||||
type: "symbol",
|
type: "symbol",
|
||||||
source: "base",
|
source: "base",
|
||||||
// New tiles build uses NaturalEarth label points layer name.
|
// A dedicated label-point layer (1 point per country) to avoid per-tile duplicates.
|
||||||
// If your tile pipeline exposes a different name, adjust here.
|
"source-layer": "country_labels",
|
||||||
"source-layer": "ne_10m_admin_0_label_points",
|
minzoom: 0,
|
||||||
minzoom: 0,
|
layout: {
|
||||||
layout: {
|
"text-field": [
|
||||||
"text-field": [
|
"coalesce",
|
||||||
"coalesce",
|
["get", "NAME_EN"],
|
||||||
["get", "sr_subunit"],
|
["get", "NAME"],
|
||||||
["get", "NAME_EN"],
|
["get", "ADMIN"],
|
||||||
["get", "NAME"],
|
["get", "name"],
|
||||||
["get", "ADMIN"],
|
"",
|
||||||
["get", "name"],
|
],
|
||||||
"",
|
|
||||||
],
|
|
||||||
"text-size": [
|
"text-size": [
|
||||||
"interpolate",
|
"interpolate",
|
||||||
["linear"],
|
["linear"],
|
||||||
["zoom"],
|
["zoom"],
|
||||||
0, 10,
|
0, 15,
|
||||||
3, 11,
|
1, 16,
|
||||||
6, 14,
|
2, 17,
|
||||||
|
4, 19,
|
||||||
|
6, 23,
|
||||||
],
|
],
|
||||||
"text-max-width": 10,
|
"text-padding": 0,
|
||||||
"text-allow-overlap": false,
|
"text-max-width": 10,
|
||||||
"symbol-placement": "point",
|
// Prefer showing labels earlier (even if it means some collisions).
|
||||||
},
|
"text-allow-overlap": true,
|
||||||
paint: {
|
"text-ignore-placement": true,
|
||||||
"text-color": "#e2e8f0",
|
"symbol-placement": "point",
|
||||||
"text-halo-color": "#0b1220",
|
},
|
||||||
"text-halo-width": 1.2,
|
paint: {
|
||||||
"text-halo-blur": 0.5,
|
"text-color": "#e2e8f0",
|
||||||
},
|
"text-halo-color": "#0b1220",
|
||||||
},
|
"text-halo-width": 1.2,
|
||||||
// Fallback for tile pipelines that expose country labels under a generic "labels" layer.
|
"text-halo-blur": 0.5,
|
||||||
// Hidden/shown together with "country-labels" toggle.
|
},
|
||||||
{
|
},
|
||||||
id: "country-labels-alt",
|
{
|
||||||
type: "symbol",
|
id: "regions-line",
|
||||||
source: "base",
|
type: "line",
|
||||||
"source-layer": "labels",
|
source: "base",
|
||||||
minzoom: 0,
|
"source-layer": "ne_10m_geography_regions_polys",
|
||||||
layout: {
|
paint: {
|
||||||
"text-field": [
|
"line-color": "#475569",
|
||||||
"coalesce",
|
"line-width": [
|
||||||
["get", "name"],
|
"interpolate",
|
||||||
["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",
|
|
||||||
["linear"],
|
["linear"],
|
||||||
["zoom"],
|
["zoom"],
|
||||||
0, 0.2,
|
0, 0.2,
|
||||||
@@ -431,25 +399,25 @@ export default function Map({
|
|||||||
"line-opacity": 0.6,
|
"line-opacity": 0.6,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "lakes-fill",
|
id: "lakes-fill",
|
||||||
type: "fill",
|
type: "fill",
|
||||||
source: "base",
|
source: "base",
|
||||||
"source-layer": "lakes",
|
"source-layer": "ne_10m_lakes",
|
||||||
paint: {
|
paint: {
|
||||||
"fill-color": "#1d4ed8",
|
"fill-color": "#1d4ed8",
|
||||||
"fill-opacity": 0.45,
|
"fill-opacity": 0.45,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "rivers-line",
|
id: "rivers-line",
|
||||||
type: "line",
|
type: "line",
|
||||||
source: "base",
|
source: "base",
|
||||||
"source-layer": "rivers",
|
"source-layer": "ne_10m_rivers_lake_centerlines",
|
||||||
paint: {
|
paint: {
|
||||||
"line-color": "#38bdf8",
|
"line-color": "#38bdf8",
|
||||||
"line-width": [
|
"line-width": [
|
||||||
"interpolate",
|
"interpolate",
|
||||||
["linear"],
|
["linear"],
|
||||||
["zoom"],
|
["zoom"],
|
||||||
0, 0.25,
|
0, 0.25,
|
||||||
@@ -459,14 +427,14 @@ export default function Map({
|
|||||||
"line-opacity": 0.85,
|
"line-opacity": 0.85,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "geolines-line",
|
id: "geolines-line",
|
||||||
type: "line",
|
type: "line",
|
||||||
source: "base",
|
source: "base",
|
||||||
"source-layer": "geolines",
|
"source-layer": "ne_10m_geographic_lines",
|
||||||
paint: {
|
paint: {
|
||||||
"line-color": "#94a3b8",
|
"line-color": "#94a3b8",
|
||||||
"line-width": 1.2,
|
"line-width": 1.2,
|
||||||
"line-opacity": 0.8,
|
"line-opacity": 0.8,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -1186,15 +1154,6 @@ function applyBackgroundLayerVisibility(
|
|||||||
visibility[layer.id] ? "visible" : "none"
|
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) {
|
function syncRasterBaseVisibility(map: maplibregl.Map, shouldShow: boolean) {
|
||||||
|
|||||||
@@ -10,11 +10,10 @@ import type { EntityWikiLinkSnapshot } from "@/uhm/types/sections";
|
|||||||
export function normalizeEditorSnapshot(raw: unknown): EditorSnapshot | null {
|
export function normalizeEditorSnapshot(raw: unknown): EditorSnapshot | null {
|
||||||
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return null;
|
||||||
const snapshot = raw as EditorSnapshot;
|
const snapshot = raw as EditorSnapshot;
|
||||||
if (
|
// Accept legacy snapshots (v1) and new ones (v2+). We only require that a FeatureCollection,
|
||||||
snapshot.editor_feature_collection &&
|
// if present, is structurally valid. Everything else is treated as optional.
|
||||||
snapshot.editor_feature_collection.type === "FeatureCollection" &&
|
const fc = (snapshot as any).editor_feature_collection as FeatureCollection | undefined;
|
||||||
Array.isArray(snapshot.editor_feature_collection.features)
|
if (fc && fc.type === "FeatureCollection" && Array.isArray(fc.features)) {
|
||||||
) {
|
|
||||||
return snapshot;
|
return snapshot;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
@@ -225,11 +224,7 @@ export function buildEditorSnapshot(options: {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
schema_version: 1,
|
schema_version: 2,
|
||||||
section: {
|
|
||||||
id: options.section.id,
|
|
||||||
title: options.section.title,
|
|
||||||
},
|
|
||||||
editor_feature_collection: JSON.parse(JSON.stringify(options.draft)) as FeatureCollection,
|
editor_feature_collection: JSON.parse(JSON.stringify(options.draft)) as FeatureCollection,
|
||||||
entities: Array.from(entityRows.values()).map((entity) => {
|
entities: Array.from(entityRows.values()).map((entity) => {
|
||||||
const id = String(entity.id || "");
|
const id = String(entity.id || "");
|
||||||
|
|||||||
@@ -60,8 +60,10 @@ export type SectionSubmission = {
|
|||||||
content?: string | null;
|
content?: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EditorSnapshot = {
|
export type EditorSnapshotV1 = {
|
||||||
schema_version: number;
|
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: {
|
section: {
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
@@ -74,6 +76,18 @@ export type EditorSnapshot = {
|
|||||||
entity_wikis?: EntityWikiLinkSnapshot[];
|
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 = {
|
export type EditorLoadResponse = {
|
||||||
section: Section;
|
section: Section;
|
||||||
state: SectionState;
|
state: SectionState;
|
||||||
|
|||||||
Reference in New Issue
Block a user