feat(geoportal-v2): map styling parity with eterra.live

Marius didn't like the previous map palette + missing labels.
Re-paint to match eterra.live's V2 map:

UATs:
  Was: #9ca3af / #6b7280 (two-tone gray)
  Now: #7c3aed violet, width ramps 0.3 → 0.6 by zoom band
  Plus new v2-uats-z8-label symbol layer that prints the UAT name
  at z9–12 (Noto Sans 10px, violet text + white halo).

Parcele (terenuri):
  Was: #0ea5e9 sky-blue line, transparent 0.001 fill (hit-only)
  Now: #22c55e green fill at 0.15 opacity (so the click area is
  visible at the same time it acts as hit-target) + #15803d
  darker green outline at 0.8 / 0.9 opacity. Cadastral-ref label
  layer renders at z17+ (small enough zoom that the polygon can
  carry a 10px label without overlapping neighbours).

Cladiri (buildings):
  Was: #fb923c orange / #ea580c dark orange
  Now: #3b82f6 blue fill at 0.5 / #1e3a5f navy line — same blue
  eterra.live uses, distinct from the green parcel underlay and
  from the violet UATs.

Cladiri „fără acte" (build_legal == 0):
  NEW. Two amber overlay layers, filtered on the tile's
  build_legal property:
    v2-cladiri-unreg-fill — #f59e0b at 0.6 opacity
    v2-cladiri-unreg-line — #b45309 at width 1
  Renders on top of the default cladiri layers so illegal builds
  visibly pop. If a PMTiles tile doesn't carry build_legal yet
  the layer is empty — no regression on legal buildings.

Building suffix label (C1, C2, C3…):
  NEW. v2-cladiri-label symbol at z16+. Mirrors eterra.live's
  expression: extract the slice after the last "-" of
  cadastral_ref ("354686-C1" → "C1"). Noto Sans 9px, navy text +
  white halo so it reads on both light and dark basemaps.

Click handler unchanged — v2-cladiri-fill covers ALL buildings
(no filter), so legal vs unreg both route through the same query.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude VM
2026-05-20 09:08:41 +03:00
parent 5fd8881571
commit 8d5316dd1b
+155 -29
View File
@@ -116,7 +116,10 @@ export const MapViewer = forwardRef<MapViewerHandle, Props>(function MapViewer(
url: `pmtiles://${PMTILES_URL}`,
});
// UAT boundaries — visible at every zoom, more prominent on low/mid zoom
// ── UAT boundaries ──
// Palette mirrors eterra.live's map: violet #7c3aed at increasing
// line-width per zoom band so the boundary stays visible from
// country view to street view without overpowering parcels.
map.addLayer({
id: "v2-uats-z5",
type: "line",
@@ -125,9 +128,9 @@ export const MapViewer = forwardRef<MapViewerHandle, Props>(function MapViewer(
minzoom: 0,
maxzoom: 8,
paint: {
"line-color": "#9ca3af",
"line-width": 0.8,
"line-opacity": 0.6,
"line-color": "#7c3aed",
"line-width": 0.3,
"line-opacity": 0.7,
},
});
map.addLayer({
@@ -138,13 +141,86 @@ export const MapViewer = forwardRef<MapViewerHandle, Props>(function MapViewer(
minzoom: 8,
maxzoom: 13,
paint: {
"line-color": "#6b7280",
"line-width": 1.2,
"line-opacity": 0.7,
"line-color": "#7c3aed",
"line-width": 0.6,
"line-opacity": 0.85,
},
});
// UAT name label at z912 (when the boundary is the main feature)
map.addLayer({
id: "v2-uats-z8-label",
type: "symbol",
source: PM_SRC,
"source-layer": "gis_uats_z8",
minzoom: 9,
maxzoom: 13,
layout: {
"text-field": ["coalesce", ["get", "name"], ""],
"text-font": ["Noto Sans Regular"],
"text-size": 10,
"text-anchor": "center",
"text-allow-overlap": false,
},
paint: {
"text-color": "#5b21b6",
"text-halo-color": "#ffffff",
"text-halo-width": 1.5,
},
});
// Buildings — fill + outline
// ── Parcele (terenuri) ──
// Green fill (#22c55e/0.15) + dark green stroke (#15803d/0.8).
// Fill provides the click-hit area without needing a separate
// transparent layer; the low opacity keeps overlapping buildings
// legible underneath.
map.addLayer({
id: "v2-terenuri-hit",
type: "fill",
source: PM_SRC,
"source-layer": "gis_terenuri",
minzoom: 13,
paint: {
"fill-color": "#22c55e",
"fill-opacity": 0.15,
},
});
map.addLayer({
id: "v2-terenuri-line",
type: "line",
source: PM_SRC,
"source-layer": "gis_terenuri",
minzoom: 13,
paint: {
"line-color": "#15803d",
"line-width": 0.8,
"line-opacity": 0.9,
},
});
// Parcel cadastral ref label — z17+ where the polygon is big
// enough that the label doesn't clutter.
map.addLayer({
id: "v2-terenuri-label",
type: "symbol",
source: PM_SRC,
"source-layer": "gis_terenuri",
minzoom: 17,
layout: {
"text-field": ["coalesce", ["get", "cadastral_ref"], ""],
"text-font": ["Noto Sans Regular"],
"text-size": 10,
"text-anchor": "center",
"text-allow-overlap": false,
"text-max-width": 8,
},
paint: {
"text-color": "#14532d",
"text-halo-color": "#ffffff",
"text-halo-width": 1.5,
},
});
// ── Cladiri (buildings) ──
// Default blue (#3b82f6/0.5 fill + #1e3a5f/0.6 stroke) at z13+.
map.addLayer({
id: "v2-cladiri-fill",
type: "fill",
@@ -152,8 +228,8 @@ export const MapViewer = forwardRef<MapViewerHandle, Props>(function MapViewer(
"source-layer": "gis_cladiri",
minzoom: 13,
paint: {
"fill-color": "#fb923c",
"fill-opacity": 0.25,
"fill-color": "#3b82f6",
"fill-opacity": 0.5,
},
});
map.addLayer({
@@ -163,34 +239,84 @@ export const MapViewer = forwardRef<MapViewerHandle, Props>(function MapViewer(
"source-layer": "gis_cladiri",
minzoom: 13,
paint: {
"line-color": "#ea580c",
"line-color": "#1e3a5f",
"line-width": 0.6,
},
});
// Parcels (terenuri) — line outline only
// ── Cladiri „fără acte" (build_legal == 0) ──
// Amber overlay (#f59e0b/0.6) + darker stroke so illegal /
// un-registered buildings pop visibly against the default blue.
// The layer renders only when the tile property `build_legal` is
// present; PMTiles overview built without that property simply
// shows nothing here (no overlap on legal buildings either).
map.addLayer({
id: "v2-terenuri-line",
type: "line",
source: PM_SRC,
"source-layer": "gis_terenuri",
minzoom: 13,
paint: {
"line-color": "#0ea5e9",
"line-width": 0.8,
"line-opacity": 0.85,
},
});
// Invisible fill for click hit-testing
map.addLayer({
id: "v2-terenuri-hit",
id: "v2-cladiri-unreg-fill",
type: "fill",
source: PM_SRC,
"source-layer": "gis_terenuri",
"source-layer": "gis_cladiri",
minzoom: 13,
filter: ["==", ["get", "build_legal"], 0],
paint: {
"fill-color": "#0ea5e9",
"fill-opacity": 0.001,
"fill-color": "#f59e0b",
"fill-opacity": 0.6,
},
});
map.addLayer({
id: "v2-cladiri-unreg-line",
type: "line",
source: PM_SRC,
"source-layer": "gis_cladiri",
minzoom: 13,
filter: ["==", ["get", "build_legal"], 0],
paint: {
"line-color": "#b45309",
"line-width": 1,
"line-opacity": 0.9,
},
});
// Building suffix label — "354686-C1" → "C1". Same logic eterra.live
// uses: slice after the last dash. z16+ so we don't clutter the
// overview when zoomed out.
map.addLayer({
id: "v2-cladiri-label",
type: "symbol",
source: PM_SRC,
"source-layer": "gis_cladiri",
minzoom: 16,
layout: {
"text-field": [
"case",
["has", "cadastral_ref"],
[
"let",
"ref",
["get", "cadastral_ref"],
[
"let",
"dashIdx",
["index-of", "-", ["var", "ref"]],
[
"case",
[">=", ["var", "dashIdx"], 0],
["slice", ["var", "ref"], ["+", ["var", "dashIdx"], 1]],
["var", "ref"],
],
],
],
"",
],
"text-font": ["Noto Sans Regular"],
"text-size": 9,
"text-anchor": "center",
"text-allow-overlap": false,
"text-max-width": 6,
},
paint: {
"text-color": "#1e3a5f",
"text-halo-color": "#ffffff",
"text-halo-width": 1.2,
},
});
}, []);