First-run magic enrichment on partially-enriched UATs can take
30+ minutes per UAT. After first complete run, subsequent runs
will be seconds.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New endpoint POST /api/eterra/refresh-all processes all 43 UATs
sequentially. UATs with >30% enrichment get magic mode, others
get base sync only. Each UAT uses the new delta engine (quick-count
+ VALID_FROM + rolling doc check). Progress tracked via progress store.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Instead of marking features enrichedAt=null and falling through to the
full enrichment flow (which downloads the entire immovable list ~5min),
the rolling doc check now merges updated PROPRIETARI/DATA_CERERE directly
into existing enrichment and returns immediately.
Also touches enrichedAt on all checked features to rotate the batch,
ensuring different features are checked on each daily run.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Always call syncLayer for TERENURI/CLADIRI (not gated by isFresh)
so that quick-count + VALID_FROM delta actually run on daily syncs
- syncLayer handles efficiency internally via quick-count match
- Add 48h freshness check for no-geom import (skip if recent)
- Admin layers: skip if synced within 24h
- Log sync summary (new features, updated features)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Cluj-Napoca (54975): base mode, parcele+cladiri only (no magic)
- Feleacu (57582): magic + no-geom (full enrichment test)
- Both with elapsed timer and phase-change logging
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Quick test button on /monitor page to trigger smart delta sync
(magic mode) on Cluj-Napoca and track progress via polling.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix: geoportal/enrich endpoint now looks up CLADIRI_ACTIVE from DB
instead of hardcoding HAS_BUILDING=0, BUILD_LEGAL=0
- Quick-count check: skip OBJECTID comparison when remote==local count
- VALID_FROM delta: detect attribute changes on existing parcels and
mark them for re-enrichment (catches spatial validity changes)
- Early bailout: skip all eTerra API calls when 0 features need enrichment
- Rolling doc check: probe 200 oldest-enriched parcels for new
documentation activity (catches ownership/CF changes VALID_FROM misses)
- Targeted doc fetch: only fetch documentation for immovable PKs that
actually need enrichment instead of all 10k+
Daily sync cost reduced from ~300+ API calls / 1-2h to ~6-10 calls / 10-15s.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Auto-deactivate monitoring when status is 'solutionat' or 'respins'
- 'Verifica acum' shows result inline (no page reload/close)
- 'Modifica' button opens edit dialog to fix petitioner/regNumber/date
- Show monitoring section even when inactive (final status visible)
- Display tracking config info (nr, date, petitioner) in detail view
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Manual trigger now only processes sync_terenuri and sync_cladiri steps.
import_nogeom and enrich are left for the regular weekend window.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Auto-poll every 15s when sync is running, 60s when idle
- Live status banner: running (with city/step), error list, weekend window waiting, connection error
- Highlight active city card and currently-running step with pulse animation
- Send immediate error email per failed step (not just at session end)
- Expose syncStatus/currentActivity/inWeekendWindow in API response
- Stop silently swallowing fetch/action errors — show them in the UI
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Simplification with 10px tolerance makes rectangular buildings look
jagged/trapezoidal at high zoom. Without it, tippecanoe still simplifies
at lower zooms via --drop-densest-as-needed but preserves full geometry
at the base zoom (z18).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PMTiles was loaded via HTTP from MinIO (10.10.10.166:9002) on an HTTPS page,
causing browser mixed-content blocking — parcels invisible on geoportal.
Fixes:
- tile-cache nginx proxies /pmtiles/ → MinIO with Range header support
- PMTILES_URL changed to relative path (resolves to HTTPS automatically)
- clickableLayers includes PMTiles fill layers (click on parcels works)
- Selection highlight uses PMTiles source at z13+ (was Martin z17+ only)
- tippecanoe per-layer zoom ranges (terenuri z13-z18, cladiri z14-z18)
skips processing millions of features at z0-z12 — faster rebuild
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Buildings now color-coded on legal status in ParcelSync map view:
- Blue fill: building has legal documents (build_legal = 1)
- Red fill: building without legal documents (build_legal = 0)
Previously only parcels had status coloring; buildings were plain blue.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- TILE-SERVER-EVALUATION.md: updated to reflect current architecture (PMTiles z0-z18)
- MODULE-MAP.md: added PMTiles + tile-cache to Geoportal section
- Monitor: timeout increased to 90 min for z18 builds, description updated
- Added PROMPT-GEOPORTAL-IMPROVE.md with mega prompt for future sessions
(includes MLT check, mvt-rs evaluation prompt, operational commands)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PMTiles now covers z0-z18 (full zoom range). Martin sources kept only for
selection highlight and fallback when PMTiles not configured.
All terenuri/cladiri fill/line/label layers served from PMTiles when active.
Zero PostGIS load for tile rendering at any zoom level.
File will be ~1-2 GB but eliminates all cold-load latency.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Without ARG/ENV declarations in the build stage, docker-compose build args
are silently ignored. webpack never sees the values → NEXT_PUBLIC_ vars
are empty → PMTiles disabled → all tiles go through Martin → PostGIS at 90% CPU.
This was the root cause of slow tile loading all along.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
NEXT_PUBLIC_ env vars are inlined by webpack at build time in client components.
Setting them only in environment (runtime) has no effect — the map-viewer
was falling back to Martin for ALL tiles, causing 90% PostgreSQL CPU.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Martin now starts at z17, so z14 sample tile returned 404.
Rebuild timeout increased from 15 to 30 min for z16 builds.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- PMTiles now covers z0-z16 (was z0-z14) for terenuri + cladiri
- Martin only serves z17-z18 (very close zoom, few features per tile)
- map-viewer: PMTiles layers serve z13-z16 for terenuri, z14-z16 for cladiri
- Labels at z16 now from PMTiles (cadastral_ref included in tiles)
- Remove failed compound index from postgis-setup.sql
This eliminates PostgreSQL as bottleneck for 99% of tile requests.
PMTiles file will be ~300-500MB (vs 104MB at z14).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Martin: pool_size=8 (prevents overwhelming PostgreSQL with concurrent queries)
- Martin: gis_terenuri minzoom 10→14, gis_cladiri minzoom 12→15
(PMTiles serves z0-z14, no point in Martin generating those)
- PostGIS: add compound index layerId+geom for Martin view queries
- PostGIS: add B-tree index on layerId for LIKE filtering in views
Fixes 90% CPU on PostgreSQL during cold tile loads at detail zoom levels.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Rebuild: shows webhook status, then polls every 15s until PMTiles
last-modified changes, then shows success with new size/timestamp
- Warm cache: shows HIT/MISS/error breakdown after completion
- Activity log panel with timestamps, color-coded status, scrollable
- 15-minute timeout on rebuild polling
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
If >80% of local features would be deleted (and >100 exist), skip deletion
and log a warning. This protects against session expiry returning empty
remote results, which previously caused the sync to mark ALL local
features as "removed" and attempt to delete them.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cluj-Napoca sync failed with 62,307 removed features exceeding the limit.
Batch deletions in chunks of 30,000 in both sync-service and no-geom-sync.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dashboard page showing:
- nginx tile-cache status (connections, requests)
- Martin tile server sources
- PMTiles file info (size, last modified)
- Cache HIT/MISS test on sample tiles
- Configuration summary
Action buttons:
- Rebuild PMTiles (triggers N8N webhook)
- Warm Cache (fetches common tiles from container)
Auto-refreshes every 30 seconds.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three bugs caused sync to return 0 features after 37 minutes:
1. reloginAttempted was instance-level flag — once set to true after first
401, all subsequent 401s threw immediately without retry. Moved to
per-request scope so each request can independently relogin on 401.
2. Session lastUsed never updated during pagination — after ~10 min of
paginating, the session store considered it expired and cleanup could
evict it. Added touchSession() call before every request.
3. Single eTerra client shared across all cities/steps for hours — now
creates a fresh client per city/step (session cache still avoids
unnecessary logins when session is valid).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- weekend-deep-sync.ts: fire webhook to N8N_WEBHOOK_URL after each sync cycle
(N8N triggers tippecanoe PMTiles rebuild via SSH on host)
- nginx tile-cache: add stub_status at /status, custom log format with cache status
- Add tile-cache-stats.sh: shows HIT/MISS ratio, cache size, slow tiles
- docker-compose: add N8N_WEBHOOK_URL env var
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cache warming must run from host, not from Docker container.
Use scripts/warm-tile-cache.sh standalone instead.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ogr2ogr doesn't auto-detect geometry from ST_SimplifyPreserveTopology().
Export raw geometry instead, let tippecanoe handle simplification (--simplification=10).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Extend PMTiles to include simplified terenuri (5m tolerance) and cladiri (3m)
- map-viewer: terenuri z13 from PMTiles, z14+ from Martin (live detail)
- map-viewer: cladiri z14 from PMTiles, z15+ from Martin
- Martin sources start at higher minzoom when PMTiles active (less DB load)
- Add warm-tile-cache.sh: pre-populate nginx cache for major cities
- Rebuild script now includes cache warming step after PMTiles upload
- Remove deprecated docker-compose version: "3.8"
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Strip parcel number prefix from building labels — "291479-C1" now displays as "C1".
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
UAT + administrativ layers now served from pre-generated PMTiles (~5ms)
instead of Martin/PostGIS (~200-2000ms) for zoom levels 0-14.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ghcr.io/felt/tippecanoe:latest returns 403 — no public Docker image.
Build tippecanoe from GitHub source in a multi-stage Alpine build.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add nginx reverse proxy cache in front of Martin (2GB, 1h TTL, stale serving, CORS)
- Martin no longer exposes host port — all traffic routed through tile-cache on :3010
- Add PMTiles support in map-viewer.tsx (conditional: NEXT_PUBLIC_PMTILES_URL env var)
- When set: single PMTiles source for UAT + administrativ layers (z0-z14, ~5ms/tile)
- When empty: fallback to Martin tile sources (existing behavior, zero breaking change)
- Add tippecanoe Docker service (profiles: tools) for on-demand PMTiles generation
- Add rebuild-overview-tiles.sh: ogr2ogr export → tippecanoe → MinIO atomic upload
- Install pmtiles npm package for MapLibre protocol registration
Performance impact:
- nginx cache: 10-100x faster on repeat tile requests, zero PostGIS load on cache hit
- PMTiles: sub-10ms overview tiles, zero PostGIS load for z0-z14
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Portainer CE volume mount pitfall (silent empty directory creation)
- Martin Docker tag format change at v1.0 (v prefix dropped)
- UNKNOWN GEOMETRY TYPE log is normal for views
- Bake-into-image pattern for config files in Portainer deployments
- Updated all implementation prompts with Portainer-safe instructions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Portainer CE deploys only docker-compose.yml — ./martin.yaml not present on host,
so Docker creates an empty directory instead of mounting the file. Solution: COPY
martin.yaml into the image at build time, eliminating the volume dependency.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Root cause: martin.yaml was never mounted in docker-compose.yml — Martin ran
in auto-discovery mode which dropped cadastral_ref from gis_cladiri tiles.
Changes:
- docker-compose: mount martin.yaml, upgrade Martin v0.15→v1.4.0, use --config
- map-viewer: add cladiriLabel layer (cadastral_ref at z16+), wire into visibility
- martin.yaml: update version comment
- geoportal/: tile server evaluation doc + 3 skill files (vector tiles, PMTiles, MapLibre perf)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New fields extracted from already-fetched documentation/GIS data
(zero extra API calls, no performance impact):
- TIP_INSCRIERE: "Intabulare, drept de PROPRIETATE, dobandit prin..."
- ACT_PROPRIETATE: "hotarare judecatoreasca nr... / contract vanzare..."
- COTA_PROPRIETATE: "1/1" or fractional
- DATA_CERERE: date of registration application
- NR_CORPURI: number of building bodies on parcel
- CORPURI_DETALII: "C1:352mp, C2:248mp, C3:104mp"
- IS_CONDOMINIUM: condominium flag
- DATA_CREARE: parcel creation date in eTerra
Also fixed HAS_BUILDING: now also uses NR_CORPURI count as fallback
(was 0 for parcels where buildingMap cross-ref missed matches).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Building labels (C1/C2/C3) disabled — Martin MVT tiles don't include
cadastral_ref as a property despite the PostgreSQL view exposing it.
Root cause needs investigation (Martin config or alternative tile server).
Removed temporary debug endpoints:
- /api/eterra/debug-tile-props
- /api/eterra/debug-tile-sample
Kept /api/eterra/debug-fields (useful long-term diagnostic).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>