perf(geoportal): zoom-dependent UAT simplification + Martin config + tile cache
PostGIS: - gis_uats view: ST_SimplifyPreserveTopology(geom, 50) + only name/county/siruta - gis_uats_simple view: ST_SimplifyPreserveTopology(geom, 500) for z0-z9 Martin config (martin.yaml): - Explicit source definitions (auto_publish: false) - gis_uats_simple (z0-9): only name+siruta, 500m simplified geometry - gis_uats (z0-14): name+siruta+county, 50m simplified - gis_terenuri (z10-18): object_id+siruta+cadastral_ref+area_value+layer_id - gis_cladiri (z12-18): same properties - 24h cache headers on all tiles MapViewer: - Dual UAT sources: simplified (z0-9) + detailed (z9+) with seamless handoff - Zoom-interpolated paint: thin lines at z5, thicker at z12 - UAT labels only z9+, fill opacity z-interpolated (0.03→0.08) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -102,3 +102,6 @@ services:
|
||||
- "3010:3000"
|
||||
environment:
|
||||
- DATABASE_URL=postgresql://architools_user:stictMyFon34!_gonY@10.10.10.166:5432/architools_db
|
||||
volumes:
|
||||
- ./martin.yaml:/config/martin.yaml
|
||||
command: ["--config", "/config/martin.yaml"]
|
||||
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
# Martin v0.15 configuration — optimized tile sources for ArchiTools Geoportal
|
||||
# All geometries are EPSG:3844 (Stereo70). Bounds are approximate Romania extent.
|
||||
|
||||
# Disable auto-discovery — only serve explicitly configured sources
|
||||
postgres:
|
||||
connection_string: ${DATABASE_URL}
|
||||
default_srid: 3844
|
||||
auto_publish: false
|
||||
tables:
|
||||
gis_uats:
|
||||
schema: public
|
||||
table: gis_uats
|
||||
geometry_column: geom
|
||||
srid: 3844
|
||||
bounds: [20.2, 43.5, 30.0, 48.3]
|
||||
minzoom: 0
|
||||
maxzoom: 14
|
||||
properties:
|
||||
name: text
|
||||
siruta: text
|
||||
|
||||
gis_uats_simple:
|
||||
schema: public
|
||||
table: gis_uats_simple
|
||||
geometry_column: geom
|
||||
srid: 3844
|
||||
bounds: [20.2, 43.5, 30.0, 48.3]
|
||||
minzoom: 0
|
||||
maxzoom: 9
|
||||
properties:
|
||||
name: text
|
||||
siruta: text
|
||||
|
||||
gis_terenuri:
|
||||
schema: public
|
||||
table: gis_terenuri
|
||||
geometry_column: geom
|
||||
srid: 3844
|
||||
bounds: [20.2, 43.5, 30.0, 48.3]
|
||||
minzoom: 10
|
||||
maxzoom: 18
|
||||
properties:
|
||||
object_id: text
|
||||
siruta: text
|
||||
cadastral_ref: text
|
||||
area_value: float8
|
||||
layer_id: text
|
||||
|
||||
gis_cladiri:
|
||||
schema: public
|
||||
table: gis_cladiri
|
||||
geometry_column: geom
|
||||
srid: 3844
|
||||
bounds: [20.2, 43.5, 30.0, 48.3]
|
||||
minzoom: 12
|
||||
maxzoom: 18
|
||||
properties:
|
||||
object_id: text
|
||||
siruta: text
|
||||
cadastral_ref: text
|
||||
area_value: float8
|
||||
layer_id: text
|
||||
@@ -158,20 +158,29 @@ WHERE geometry IS NOT NULL AND geom IS NULL;
|
||||
CREATE INDEX IF NOT EXISTS gis_uat_geom_idx
|
||||
ON "GisUat" USING GIST (geom);
|
||||
|
||||
-- 8. Martin/QGIS-friendly view
|
||||
-- 8. Martin/QGIS-friendly view (moderate simplification — 50m tolerance)
|
||||
CREATE OR REPLACE VIEW gis_uats AS
|
||||
SELECT
|
||||
siruta,
|
||||
name,
|
||||
county,
|
||||
"workspacePk" AS workspace_pk,
|
||||
"areaValue" AS area_value,
|
||||
geom
|
||||
ST_SimplifyPreserveTopology(geom, 50) AS geom
|
||||
FROM "GisUat"
|
||||
WHERE geom IS NOT NULL;
|
||||
|
||||
-- 9. Simplified view for low zoom levels (z5-z9) — 500m tolerance
|
||||
CREATE OR REPLACE VIEW gis_uats_simple AS
|
||||
SELECT
|
||||
siruta,
|
||||
name,
|
||||
ST_SimplifyPreserveTopology(geom, 500) AS geom
|
||||
FROM "GisUat"
|
||||
WHERE geom IS NOT NULL;
|
||||
|
||||
-- =============================================================================
|
||||
-- Done! Martin will auto-discover gis_uats at http://localhost:3010/gis_uats
|
||||
-- Done! Martin serves these views as vector tiles:
|
||||
-- - gis_uats (moderate detail, z9+)
|
||||
-- - gis_uats_simple (coarse overview, z5-z9)
|
||||
-- QGIS: PostgreSQL -> 10.10.10.166:5432 / architools_db -> gis_uats view
|
||||
-- SRID: 3844 (Stereo70)
|
||||
-- =============================================================================
|
||||
|
||||
@@ -34,6 +34,7 @@ const DEFAULT_ZOOM = 7;
|
||||
|
||||
/** Source/layer IDs used on the map */
|
||||
const SOURCES = {
|
||||
uatsSimple: "gis_uats_simple",
|
||||
uats: "gis_uats",
|
||||
terenuri: "gis_terenuri",
|
||||
cladiri: "gis_cladiri",
|
||||
@@ -41,6 +42,8 @@ const SOURCES = {
|
||||
|
||||
/** 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",
|
||||
@@ -232,7 +235,7 @@ export const MapViewer = forwardRef<MapViewerHandle, MapViewerProps>(
|
||||
if (!map || !map.isStyleLoaded()) return;
|
||||
|
||||
const mapping: Record<string, string[]> = {
|
||||
uats: [LAYER_IDS.uatsFill, LAYER_IDS.uatsLine, LAYER_IDS.uatsLabel],
|
||||
uats: [LAYER_IDS.uatsSimpleFill, LAYER_IDS.uatsSimpleLine, LAYER_IDS.uatsFill, LAYER_IDS.uatsLine, LAYER_IDS.uatsLabel],
|
||||
terenuri: [LAYER_IDS.terenuriFill, LAYER_IDS.terenuriLine],
|
||||
cladiri: [LAYER_IDS.cladiriFill, LAYER_IDS.cladiriLine],
|
||||
};
|
||||
@@ -278,11 +281,51 @@ export const MapViewer = forwardRef<MapViewerHandle, MapViewerProps>(
|
||||
|
||||
/* ---- Add Martin sources + layers on load ---- */
|
||||
map.on("load", () => {
|
||||
// --- UAT boundaries ---
|
||||
// --- UAT boundaries (simplified, low zoom z0-z9) ---
|
||||
map.addSource(SOURCES.uatsSimple, {
|
||||
type: "vector",
|
||||
tiles: [`${resolvedMartinUrl}/${SOURCES.uatsSimple}/{z}/{x}/{y}`],
|
||||
minzoom: 0,
|
||||
maxzoom: 9,
|
||||
});
|
||||
|
||||
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,
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
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, {
|
||||
type: "vector",
|
||||
tiles: [`${resolvedMartinUrl}/${SOURCES.uats}/{z}/{x}/{y}`],
|
||||
minzoom: 0,
|
||||
minzoom: 9,
|
||||
maxzoom: 16,
|
||||
});
|
||||
|
||||
@@ -291,9 +334,14 @@ export const MapViewer = forwardRef<MapViewerHandle, MapViewerProps>(
|
||||
type: "fill",
|
||||
source: SOURCES.uats,
|
||||
"source-layer": SOURCES.uats,
|
||||
minzoom: 8,
|
||||
paint: {
|
||||
"fill-color": "#8b5cf6",
|
||||
"fill-opacity": 0.05,
|
||||
"fill-opacity": [
|
||||
"interpolate", ["linear"], ["zoom"],
|
||||
8, 0.03,
|
||||
12, 0.08,
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
@@ -302,9 +350,15 @@ export const MapViewer = forwardRef<MapViewerHandle, MapViewerProps>(
|
||||
type: "line",
|
||||
source: SOURCES.uats,
|
||||
"source-layer": SOURCES.uats,
|
||||
minzoom: 9,
|
||||
paint: {
|
||||
"line-color": "#7c3aed",
|
||||
"line-width": 1.5,
|
||||
"line-width": [
|
||||
"interpolate", ["linear"], ["zoom"],
|
||||
5, 0.5,
|
||||
8, 1,
|
||||
12, 2,
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
@@ -313,9 +367,14 @@ export const MapViewer = forwardRef<MapViewerHandle, MapViewerProps>(
|
||||
type: "symbol",
|
||||
source: SOURCES.uats,
|
||||
"source-layer": SOURCES.uats,
|
||||
minzoom: 9,
|
||||
layout: {
|
||||
"text-field": ["coalesce", ["get", "name"], ["get", "uat_name"], ""],
|
||||
"text-size": 12,
|
||||
"text-size": [
|
||||
"interpolate", ["linear"], ["zoom"],
|
||||
9, 10,
|
||||
14, 14,
|
||||
],
|
||||
"text-anchor": "center",
|
||||
"text-allow-overlap": false,
|
||||
},
|
||||
@@ -431,6 +490,7 @@ export const MapViewer = forwardRef<MapViewerHandle, MapViewerProps>(
|
||||
LAYER_IDS.terenuriFill,
|
||||
LAYER_IDS.cladiriFill,
|
||||
LAYER_IDS.uatsFill,
|
||||
LAYER_IDS.uatsSimpleFill,
|
||||
];
|
||||
|
||||
map.on("click", (e) => {
|
||||
|
||||
Reference in New Issue
Block a user