perf(geoportal): 4-level UAT simplification + intravilan layer + preserve view on basemap switch
UAT zoom-dependent views (read-only, original geom NEVER modified): - gis_uats_z0 (z0-5): 2000m simplification — country outlines - gis_uats_z5 (z5-8): 500m — regional overview - gis_uats_z8 (z8-12): 50m — county/city level with labels - gis_uats_z12 (z12+): 10m — near-original precision New layers: - gis_administrativ (intravilan, arii speciale) — orange dashed, no simplification - Toggle in layer panel (off by default) Basemap switching: - Now preserves current center + zoom when switching between basemaps Parcels + buildings: NO simplification (exact geometry needed) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -42,6 +42,13 @@ const LAYER_GROUPS: LayerGroupDef[] = [
|
||||
color: "#3b82f6",
|
||||
defaultVisible: true,
|
||||
},
|
||||
{
|
||||
id: "administrativ",
|
||||
label: "Intravilan",
|
||||
description: "Limite intravilan, arii speciale (zoom >= 10)",
|
||||
color: "#ea580c",
|
||||
defaultVisible: false,
|
||||
},
|
||||
];
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
||||
@@ -34,19 +34,28 @@ const DEFAULT_ZOOM = 7;
|
||||
|
||||
/** Source/layer IDs used on the map */
|
||||
const SOURCES = {
|
||||
uatsSimple: "gis_uats_simple",
|
||||
uats: "gis_uats",
|
||||
uatsZ0: "gis_uats_z0", // z0-5: 2000m simplification
|
||||
uatsZ5: "gis_uats_z5", // z5-8: 500m
|
||||
uatsZ8: "gis_uats_z8", // z8-12: 50m
|
||||
uatsZ12: "gis_uats_z12", // z12+: 10m (near-original)
|
||||
terenuri: "gis_terenuri",
|
||||
cladiri: "gis_cladiri",
|
||||
administrativ: "gis_administrativ",
|
||||
} as const;
|
||||
|
||||
/** Map layer IDs (prefixed to avoid collisions) */
|
||||
const LAYER_IDS = {
|
||||
uatsSimpleFill: "layer-uats-simple-fill",
|
||||
uatsSimpleLine: "layer-uats-simple-line",
|
||||
uatsFill: "layer-uats-fill",
|
||||
uatsLine: "layer-uats-line",
|
||||
uatsLabel: "layer-uats-label",
|
||||
uatsZ0Line: "layer-uats-z0-line",
|
||||
uatsZ5Fill: "layer-uats-z5-fill",
|
||||
uatsZ5Line: "layer-uats-z5-line",
|
||||
uatsZ8Fill: "layer-uats-z8-fill",
|
||||
uatsZ8Line: "layer-uats-z8-line",
|
||||
uatsZ8Label: "layer-uats-z8-label",
|
||||
uatsZ12Fill: "layer-uats-z12-fill",
|
||||
uatsZ12Line: "layer-uats-z12-line",
|
||||
uatsZ12Label: "layer-uats-z12-label",
|
||||
adminFill: "layer-admin-fill",
|
||||
adminLine: "layer-admin-line",
|
||||
terenuriFill: "layer-terenuri-fill",
|
||||
terenuriLine: "layer-terenuri-line",
|
||||
cladiriFill: "layer-cladiri-fill",
|
||||
@@ -235,7 +244,13 @@ export const MapViewer = forwardRef<MapViewerHandle, MapViewerProps>(
|
||||
if (!map || !map.isStyleLoaded()) return;
|
||||
|
||||
const mapping: Record<string, string[]> = {
|
||||
uats: [LAYER_IDS.uatsSimpleFill, LAYER_IDS.uatsSimpleLine, LAYER_IDS.uatsFill, LAYER_IDS.uatsLine, LAYER_IDS.uatsLabel],
|
||||
uats: [
|
||||
LAYER_IDS.uatsZ0Line,
|
||||
LAYER_IDS.uatsZ5Fill, LAYER_IDS.uatsZ5Line,
|
||||
LAYER_IDS.uatsZ8Fill, LAYER_IDS.uatsZ8Line, LAYER_IDS.uatsZ8Label,
|
||||
LAYER_IDS.uatsZ12Fill, LAYER_IDS.uatsZ12Line, LAYER_IDS.uatsZ12Label,
|
||||
],
|
||||
administrativ: [LAYER_IDS.adminFill, LAYER_IDS.adminLine],
|
||||
terenuri: [LAYER_IDS.terenuriFill, LAYER_IDS.terenuriLine],
|
||||
cladiri: [LAYER_IDS.cladiriFill, LAYER_IDS.cladiriLine],
|
||||
};
|
||||
@@ -263,13 +278,18 @@ export const MapViewer = forwardRef<MapViewerHandle, MapViewerProps>(
|
||||
useEffect(() => {
|
||||
if (!containerRef.current) return;
|
||||
|
||||
// Preserve current view when switching basemaps
|
||||
const prevMap = mapRef.current;
|
||||
const currentCenter = prevMap ? prevMap.getCenter().toArray() as [number, number] : (center ?? DEFAULT_CENTER);
|
||||
const currentZoom = prevMap ? prevMap.getZoom() : (zoom ?? DEFAULT_ZOOM);
|
||||
|
||||
const basemapDef = BASEMAPS[basemap];
|
||||
|
||||
const map = new maplibregl.Map({
|
||||
container: containerRef.current,
|
||||
style: buildStyle(basemapDef),
|
||||
center: center ?? DEFAULT_CENTER,
|
||||
zoom: zoom ?? DEFAULT_ZOOM,
|
||||
center: currentCenter,
|
||||
zoom: currentZoom,
|
||||
maxZoom: basemapDef.maxzoom ?? 20,
|
||||
});
|
||||
|
||||
@@ -281,111 +301,118 @@ export const MapViewer = forwardRef<MapViewerHandle, MapViewerProps>(
|
||||
|
||||
/* ---- Add Martin sources + layers on load ---- */
|
||||
map.on("load", () => {
|
||||
// --- UAT boundaries (simplified, low zoom z0-z9) ---
|
||||
map.addSource(SOURCES.uatsSimple, {
|
||||
const m = resolvedMartinUrl;
|
||||
|
||||
// === UAT z0-5: very coarse (2000m) — lines only ===
|
||||
map.addSource(SOURCES.uatsZ0, {
|
||||
type: "vector",
|
||||
tiles: [`${resolvedMartinUrl}/${SOURCES.uatsSimple}/{z}/{x}/{y}`],
|
||||
minzoom: 0,
|
||||
maxzoom: 9,
|
||||
tiles: [`${m}/${SOURCES.uatsZ0}/{z}/{x}/{y}`],
|
||||
minzoom: 0, maxzoom: 5,
|
||||
});
|
||||
|
||||
map.addLayer({
|
||||
id: LAYER_IDS.uatsSimpleFill,
|
||||
type: "fill",
|
||||
source: SOURCES.uatsSimple,
|
||||
"source-layer": SOURCES.uatsSimple,
|
||||
maxzoom: 9,
|
||||
paint: {
|
||||
"fill-color": "#8b5cf6",
|
||||
"fill-opacity": [
|
||||
"interpolate", ["linear"], ["zoom"],
|
||||
5, 0.03,
|
||||
8, 0.05,
|
||||
],
|
||||
},
|
||||
id: LAYER_IDS.uatsZ0Line, type: "line",
|
||||
source: SOURCES.uatsZ0, "source-layer": SOURCES.uatsZ0,
|
||||
maxzoom: 5,
|
||||
paint: { "line-color": "#7c3aed", "line-width": 0.3 },
|
||||
});
|
||||
|
||||
map.addLayer({
|
||||
id: LAYER_IDS.uatsSimpleLine,
|
||||
type: "line",
|
||||
source: SOURCES.uatsSimple,
|
||||
"source-layer": SOURCES.uatsSimple,
|
||||
maxzoom: 9,
|
||||
paint: {
|
||||
"line-color": "#7c3aed",
|
||||
"line-width": [
|
||||
"interpolate", ["linear"], ["zoom"],
|
||||
5, 0.5,
|
||||
8, 1,
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
// --- UAT boundaries (detailed, high zoom z9+) ---
|
||||
map.addSource(SOURCES.uats, {
|
||||
// === UAT z5-8: coarse (500m) — lines + faint fill ===
|
||||
map.addSource(SOURCES.uatsZ5, {
|
||||
type: "vector",
|
||||
tiles: [`${resolvedMartinUrl}/${SOURCES.uats}/{z}/{x}/{y}`],
|
||||
minzoom: 9,
|
||||
maxzoom: 16,
|
||||
tiles: [`${m}/${SOURCES.uatsZ5}/{z}/{x}/{y}`],
|
||||
minzoom: 5, maxzoom: 8,
|
||||
});
|
||||
map.addLayer({
|
||||
id: LAYER_IDS.uatsZ5Fill, type: "fill",
|
||||
source: SOURCES.uatsZ5, "source-layer": SOURCES.uatsZ5,
|
||||
minzoom: 5, maxzoom: 8,
|
||||
paint: { "fill-color": "#8b5cf6", "fill-opacity": 0.03 },
|
||||
});
|
||||
map.addLayer({
|
||||
id: LAYER_IDS.uatsZ5Line, type: "line",
|
||||
source: SOURCES.uatsZ5, "source-layer": SOURCES.uatsZ5,
|
||||
minzoom: 5, maxzoom: 8,
|
||||
paint: { "line-color": "#7c3aed", "line-width": 0.6 },
|
||||
});
|
||||
|
||||
map.addLayer({
|
||||
id: LAYER_IDS.uatsFill,
|
||||
type: "fill",
|
||||
source: SOURCES.uats,
|
||||
"source-layer": SOURCES.uats,
|
||||
minzoom: 8,
|
||||
paint: {
|
||||
"fill-color": "#8b5cf6",
|
||||
"fill-opacity": [
|
||||
"interpolate", ["linear"], ["zoom"],
|
||||
8, 0.03,
|
||||
12, 0.08,
|
||||
],
|
||||
},
|
||||
// === UAT z8-12: moderate (50m) — lines + fill + labels ===
|
||||
map.addSource(SOURCES.uatsZ8, {
|
||||
type: "vector",
|
||||
tiles: [`${m}/${SOURCES.uatsZ8}/{z}/{x}/{y}`],
|
||||
minzoom: 8, maxzoom: 12,
|
||||
});
|
||||
|
||||
map.addLayer({
|
||||
id: LAYER_IDS.uatsLine,
|
||||
type: "line",
|
||||
source: SOURCES.uats,
|
||||
"source-layer": SOURCES.uats,
|
||||
minzoom: 9,
|
||||
paint: {
|
||||
"line-color": "#7c3aed",
|
||||
"line-width": [
|
||||
"interpolate", ["linear"], ["zoom"],
|
||||
5, 0.5,
|
||||
8, 1,
|
||||
12, 2,
|
||||
],
|
||||
},
|
||||
id: LAYER_IDS.uatsZ8Fill, type: "fill",
|
||||
source: SOURCES.uatsZ8, "source-layer": SOURCES.uatsZ8,
|
||||
minzoom: 8, maxzoom: 12,
|
||||
paint: { "fill-color": "#8b5cf6", "fill-opacity": 0.05 },
|
||||
});
|
||||
|
||||
map.addLayer({
|
||||
id: LAYER_IDS.uatsLabel,
|
||||
type: "symbol",
|
||||
source: SOURCES.uats,
|
||||
"source-layer": SOURCES.uats,
|
||||
minzoom: 9,
|
||||
id: LAYER_IDS.uatsZ8Line, type: "line",
|
||||
source: SOURCES.uatsZ8, "source-layer": SOURCES.uatsZ8,
|
||||
minzoom: 8, maxzoom: 12,
|
||||
paint: { "line-color": "#7c3aed", "line-width": 1 },
|
||||
});
|
||||
map.addLayer({
|
||||
id: LAYER_IDS.uatsZ8Label, type: "symbol",
|
||||
source: SOURCES.uatsZ8, "source-layer": SOURCES.uatsZ8,
|
||||
minzoom: 9, maxzoom: 12,
|
||||
layout: {
|
||||
"text-field": ["coalesce", ["get", "name"], ["get", "uat_name"], ""],
|
||||
"text-size": [
|
||||
"interpolate", ["linear"], ["zoom"],
|
||||
9, 10,
|
||||
14, 14,
|
||||
],
|
||||
"text-anchor": "center",
|
||||
"text-allow-overlap": false,
|
||||
},
|
||||
paint: {
|
||||
"text-color": "#5b21b6",
|
||||
"text-halo-color": "#ffffff",
|
||||
"text-halo-width": 1.5,
|
||||
"text-field": ["coalesce", ["get", "name"], ""],
|
||||
"text-size": 10, "text-anchor": "center", "text-allow-overlap": false,
|
||||
},
|
||||
paint: { "text-color": "#5b21b6", "text-halo-color": "#fff", "text-halo-width": 1.5 },
|
||||
});
|
||||
|
||||
// --- Terenuri (parcels) ---
|
||||
// === UAT z12+: fine (10m) — full detail ===
|
||||
map.addSource(SOURCES.uatsZ12, {
|
||||
type: "vector",
|
||||
tiles: [`${m}/${SOURCES.uatsZ12}/{z}/{x}/{y}`],
|
||||
minzoom: 12, maxzoom: 16,
|
||||
});
|
||||
map.addLayer({
|
||||
id: LAYER_IDS.uatsZ12Fill, type: "fill",
|
||||
source: SOURCES.uatsZ12, "source-layer": SOURCES.uatsZ12,
|
||||
minzoom: 12,
|
||||
paint: { "fill-color": "#8b5cf6", "fill-opacity": 0.08 },
|
||||
});
|
||||
map.addLayer({
|
||||
id: LAYER_IDS.uatsZ12Line, type: "line",
|
||||
source: SOURCES.uatsZ12, "source-layer": SOURCES.uatsZ12,
|
||||
minzoom: 12,
|
||||
paint: { "line-color": "#7c3aed", "line-width": 2 },
|
||||
});
|
||||
map.addLayer({
|
||||
id: LAYER_IDS.uatsZ12Label, type: "symbol",
|
||||
source: SOURCES.uatsZ12, "source-layer": SOURCES.uatsZ12,
|
||||
minzoom: 12,
|
||||
layout: {
|
||||
"text-field": ["coalesce", ["get", "name"], ""],
|
||||
"text-size": 13, "text-anchor": "center", "text-allow-overlap": false,
|
||||
},
|
||||
paint: { "text-color": "#5b21b6", "text-halo-color": "#fff", "text-halo-width": 1.5 },
|
||||
});
|
||||
|
||||
// === Administrativ (intravilan, arii speciale) ===
|
||||
map.addSource(SOURCES.administrativ, {
|
||||
type: "vector",
|
||||
tiles: [`${m}/${SOURCES.administrativ}/{z}/{x}/{y}`],
|
||||
minzoom: 10, maxzoom: 16,
|
||||
});
|
||||
map.addLayer({
|
||||
id: LAYER_IDS.adminFill, type: "fill",
|
||||
source: SOURCES.administrativ, "source-layer": SOURCES.administrativ,
|
||||
minzoom: 11,
|
||||
paint: { "fill-color": "#f97316", "fill-opacity": 0.06 },
|
||||
});
|
||||
map.addLayer({
|
||||
id: LAYER_IDS.adminLine, type: "line",
|
||||
source: SOURCES.administrativ, "source-layer": SOURCES.administrativ,
|
||||
minzoom: 10,
|
||||
paint: { "line-color": "#ea580c", "line-width": 1.2, "line-dasharray": [4, 2] },
|
||||
});
|
||||
|
||||
// --- Terenuri (parcels) — NO simplification ---
|
||||
map.addSource(SOURCES.terenuri, {
|
||||
type: "vector",
|
||||
tiles: [`${resolvedMartinUrl}/${SOURCES.terenuri}/{z}/{x}/{y}`],
|
||||
@@ -489,8 +516,9 @@ export const MapViewer = forwardRef<MapViewerHandle, MapViewerProps>(
|
||||
const clickableLayers = [
|
||||
LAYER_IDS.terenuriFill,
|
||||
LAYER_IDS.cladiriFill,
|
||||
LAYER_IDS.uatsFill,
|
||||
LAYER_IDS.uatsSimpleFill,
|
||||
LAYER_IDS.uatsZ5Fill,
|
||||
LAYER_IDS.uatsZ8Fill,
|
||||
LAYER_IDS.uatsZ12Fill,
|
||||
];
|
||||
|
||||
map.on("click", (e) => {
|
||||
|
||||
Reference in New Issue
Block a user