feat/atlas-scan
needs attentionviewing older commit2f8cf79 · incrementalpre-PRreviewed 2026-07-18 00:59 UTC1H · 3M · 4L · 3I- Purpose
- Build a private geospatial siting explorer (Atlas) for Batu's solar/energy prospects — layers for gas pipelines, aquifers, industrial parks, and private generation overlaid on Mexican transmission infrastructure
- Goal
- Add owner/developer enrichment and Batu-substation proximity to the industrial parks layer so Batu's commercial team can identify high-value prospects by location and grid access
- Sub-goals
- SG-1: Enrich 201 industrial parks with verified owner/developer (101 identified: Vesta, FINSA, VYNMSA, Hines, state trusts)
- SG-2: Replace OSM-substation proximity (gkm) with Batu's own substation dataset (ghub_km/ghub_n/ghub_kv/ghub_sat)
- SG-3: Show substation saturation as a siting signal in the popup
- What
- parques_industriales.geojson enriched with owner/oconf/ncanon/ghub_* fields (201 features, down from 256); index.html popup updated to display owner and Batu-substation details; circle-color paint expression switched from gkm to ghub_km
- Why
- OSM substations are a rough proxy; Batu's own substation data (with saturation) gives a direct grid-access signal. Owner enrichment enables commercial targeting without leaving the map.
- Areas
- apps/web/public/atlas/data/parques_industriales.geojson+1−1apps/web/public/atlas/index.html+31−5
- Blast
- 2 files, +32/-6 lines; private internal tool only — no public routes, no Next.js components, no API changes
Findings · 11
correctness2
ghub_sat displayed as fraction with % suffix — '0.49%' instead of '49%'
apps/web/public/atlas/index.html:316
ghub_sat is stored as a decimal fraction (e.g. 0.49, 1.51, 2.51) but the popup appends '%' verbatim: `${p.ghub_sat}%`. Three features exceed 1.0 (up to 2.51), so display values are both wrong and inconsistent. The hubs popup for the same substations shows bare numbers without '%'. Either multiply by 100 before display (`${(+p.ghub_sat*100).toFixed(0)}%`) or store as percentage integers in the GeoJSON.
ghub_sat '0' displayed as '0%' for ~141 features — ambiguous sentinel vs genuine zero
apps/web/public/atlas/index.html:316
The guard `p.ghub_sat!==''` suppresses features with no data (38 features) but passes for the string '0', causing ~141 features to display 'saturación de esa S.E.: 0%'. If '0' is a missing-data sentinel same as '' it should be suppressed; if it's a genuine reading, it should be documented. Clarify the encoding to remove the ambiguity.
security2
Unsanitized GeoJSON properties interpolated into setHTML — stored XSS potential
apps/web/public/atlas/index.html:316
p.owner, p.ncanon, p.n, p.edo, p.oconf, p.ghub_n, p.ghub_kv, and p.ghub_sat are interpolated directly into HTML passed to mapboxgl.Popup.setHTML() (sets innerHTML). Current values are safe, but owner data from web research has the loosest provenance of any atlas field. A future data update with '<script>' or an onerror= attribute would execute with no CSP to block it. Pre-existing pattern across all popup handlers, but this commit extends it with the highest-risk field (owner). Fix: add a small htmlEscape() helper or switch to DOM construction with textContent.
No Content-Security-Policy on the atlas page
apps/web/public/atlas/index.html
The standalone HTML has no CSP meta tag, and neither vercel.json nor next.config.ts injects one for /atlas/*. If XSS is triggered (see finding above), there is no browser policy to restrict script execution or data exfiltration. Adding script-src 'self' cdn.jsdelivr.net and connect-src limited to known tile/API hosts would be meaningful defense-in-depth.
conventions2
meta.source still references 'OSM substation proximity' after migrating to Batu substations
apps/web/public/atlas/data/parques_industriales.geojson
The meta.source field reads 'nearest OSM substation proximity' but ghub_* fields are sourced from Batu's own substation dataset (popup footer confirms 'S.E.: Batu'). The old OSM-proximity fields (gkm/sub/skv) are now dead. Stale provenance metadata matters for a siting tool where data lineage is audited. Update to: 'OpenStreetMap via Overpass (named industrial parks) + nearest Batu transmission substation (CFE/CENACE data)'.
oconf suppression logic hides only high-confidence owners — inverted from typical UX
apps/web/public/atlas/index.html:316
The popup shows oconf only when it is NOT 'high': low/medium confidence shows a warning, high confidence shows nothing. Defensible (high needs no caveat), but opposite of most UX patterns. A brief inline comment clarifying intent (`// only flag sub-high confidence`) would prevent a future editor from reading this as a bug.
tests2
No automated test guards ghub_sat unit interpretation
apps/web/public/atlas/data/parques_industriales.geojson
A 15-line Vitest assertion importing the GeoJSON and checking: all features have ghub_km/ghub_n as non-null; ghub_sat when non-empty parses as float; owner and oconf co-present or both absent. Would catch the unit bug above at PR time.
Feature count change 256→201 unexplained in code or UI
apps/web/public/atlas/data/parques_industriales.geojson
55 features were removed. The legend text correctly says 201 but does not explain the reduction. No assertion guards the count. If the reduction was deduplication, a comment in the data pipeline script or the commit message would provide auditability.
improvement3
Orphaned gkm/sub/skv fields in all 201 GeoJSON features — dead weight
apps/web/public/atlas/data/parques_industriales.geojson
All 201 features still carry gkm, sub, and skv from the OSM-proximity era. The HTML renders none of them. 34 features have non-empty sub values pointing to different substations than ghub_n — a reader inspecting the raw GeoJSON cannot tell which proximity is authoritative. Remove or mark deprecated to prevent confusion and reduce payload size.
p.ghub_sat !== undefined guard is always true — redundant condition
apps/web/public/atlas/index.html:316
Every GeoJSON feature includes the ghub_sat key, so the undefined check never filters anything. Simplify to `if(p.ghub_sat !== '')` to make intent clear.
ghub_sat and ghub_kv stored as strings instead of JSON numbers
apps/web/public/atlas/data/parques_industriales.geojson
Both fields are numeric in meaning but serialized as strings (e.g. '0.49', '230'). Storing as JSON numbers aligns with GeoJSON convention, enables numeric MapLibre paint expressions, and removes implicit coercions in future consumers.
History · 16 commits
- 91aaedfsafeincremental0H · 1M · 1L2026-08-12 17:35
- cb8d915needs attentionincremental2H · 5M · 3L2026-08-12 17:29
- 4cbbe8aneeds attentionincremental0H · 3M · 8L2026-08-12 14:01
- 4aa3c02needs attentionincremental2H · 3M · 3L2026-08-12 02:35
- f5630b2needs attentionincremental0H · 4M · 5L2026-08-12 02:17
- 28fde5bneeds attentionincremental1H · 2M · 2L2026-08-12 01:53
- 0babe51needs attentionincremental1H · 2M · 5L2026-08-12 01:23
- 50e8a8cneeds attentionincremental3H · 5M · 7L2026-08-11 23:58
- 3af4686needs attentionincremental3H · 5M · 5L2026-08-03 20:07
- f5d3266needs attentionincremental0H · 3M · 8L2026-08-03 19:40
- 9f8b61aneeds attentionincremental0H · 2M · 3L2026-08-03 19:15
- ea51fa0needs attentionincremental1H · 4M · 4L2026-08-03 19:02
- 2b33f2fneeds attentionincremental0H · 1M · 1L2026-07-18 05:09
- 2f8cf79needs attentionincremental1H · 3M · 4L2026-07-18 00:59current
- 1616332safeincremental0H · 0M · 1L2026-07-18 00:22
- e997fd8needs attentionfull1H · 4M · 6L2026-07-17 23:53