Commit History
-
fix: graceful fallback when indicator has no grid overlay
KSvend +1: Claude Opus 4.6 (1M context)The overlay was disappearing completely when clicking between indicators if any of them hit the raster→grid fallback path — the frontend fell through to _renderStatusOverlay() which (after the AOI rectangle was removed) did nothing visible, leaving a blank map with no feedback. Backend: - raster_to_grid_dict() now handles all-NaN rasters by emitting an empty grid so the legend still renders instead of returning None - _save_spatial_json() logs the raster path + band + existence when serializing, and writes a richer raster-unavailable payload (with label, colormap, vmin, vmax) so the frontend can still show a meaningful legend Frontend: - renderSpatialOverlay() no longer falls through to the old status overlay — unknown/fallback payloads render the legend in its new "overlay not available" mode with the product name + color hint - new _activeProductId tracking drops stale spatial responses when the user clicks through indicators rapidly - addSource/addLayer calls are wra…ef89b38 -
feat: remove AOI box, add map legend, color hints, before/after bars
KSvend +1: Claude Opus 4.6 (1M context)Polish pass on the results dashboard based on user feedback: - the AOI rectangle is gone from the results map — it was clashing with the indicator overlay for no informational gain; the overlay itself defines the area - new floating map legend (bottom-right) shows the active indicator label, the exact color gradient, the value range, and a one-line plain-language hint like "Green = healthier than baseline, pink = stressed, cleared, or burnt" - each indicator card now carries its own color hint under the chart, so non-tech readers don't need to consult the map legend to interpret what they're looking at - two-point chart_data (the built-up baseline → current payload) is now rendered as a before/after bar pair with a percent-change badge, instead of a meaningless two-dot line - MapLibre ScaleControl added in the bottom-left so distances are legible at a glance - grid overlay opacity softened from 0.65 → 0.55 so basemap labels remain readable - product color hints c…1ea9510 -
feat: show real indicator overlay on dashboard map + fix downloads
KSvend +1: Claude Opus 4.6 (1M context)The results dashboard map was only tinting the AOI box because the frontend had no handler for map_type="raster" — all four indicators use raster spatial data, so nothing ever rendered. Map overlay: - new app/outputs/spatial_web.raster_to_grid_dict() downsamples the product raster (using NaN-aware block mean) to ~96x96, flips rows to match the frontend's ascending-lat grid convention, and emits explicit matplotlib-colormap hex stops so the browser does not need to know RdYlGn, Blues, PiYG, etc. - _save_spatial_json() converts raster SpatialData through the helper before writing, so indicators return a grid-format payload the existing _renderGrid() already understands - _renderGrid() now prefers backend-provided color stops and fits the map view to the grid extent so the overlay is always visible Download fixes: - authDownload(): anchor is appended to the DOM before .click() and objectURL revocation is deferred — the old synchronous teardown broke in Safari/Firefox a…d2a9a16
-
feat: rich results dashboard with inline sparklines and overview
KSvend +1: Claude Opus 4.6 (1M context)Previously the results page showed only a map and tiny cards with a single headline, so users had to download the PDF to see any actual findings. Every ProductResult already contained chart_data, z-scores, confidence factors, summary text, and limitations — they were just being thrown away by the frontend. Backend: - new GET /api/jobs/{id}/overview serves overview_score.json + compound_signals.json (both already written at job-complete time) Frontend: - new getJobOverview() helper in api.js - results.js rewritten to render: - Overview header: composite score ring, plain-language headline, clickable per-indicator chip strip, compound signals banner - Rich indicator cards with: - name + question, status badge, trend glyph, confidence dot - inline SVG sparkline (baseline envelope, mean line, current line, anomaly markers) — no external chart library - z-score / anomaly months / hotspot % metrics row - full summary paragraph (no truncation) - 4-factor …57ba197
-
refactor: rename "indicators" to "EO products" throughout
KSvend +2: Claude, HappyFull rename across codebase: - app/indicators/ → app/eo_products/ - IndicatorResult → ProductResult, IndicatorMeta → ProductMeta - IndicatorRegistry → ProductRegistry, BaseIndicator → BaseProduct - NdviIndicator → NdviProduct (and Water, SAR, Buildup) - indicator_id → product_id in models, APIs, and frontend - /api/indicators → /api/eo-products - All user-facing text updated - Frontend page/element IDs updated - 27 tests passing Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>df6bf75 -
fix: re-register map click handler after basemap toggle
KSvend +2: Claude, HappysetStyle() strips all event listeners, so toggling the basemap killed the AOI click-to-place handler and crosshair cursor. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>52dc51f -
fix: address code review — race condition, listener leak, HTML nesting, client caching
KSvend +2: Claude, Happy- Add _advisorRequestId guard to prevent stale advisor responses from overwriting newer ones on rapid clicks - Move all event listeners (size toggles, geocoder, season hints) inside the _aoiMapInit guard to prevent accumulation on re-navigation - Fix advisor card HTML nesting — sibling of form-group, not child - Cache Anthropic client at module level for connection pooling - Fix latitude label to correctly show °S for southern hemisphere - Update landing page copy from "Draw" to "Select" Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>b8c5bd8 -
feat: wire AOI advisor into Define Area page
KSvendbaf9ff1 -
feat: add advisor card HTML and CSS styles
KSvend +2: Claude, HappyGenerated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>526cf11 -
feat: add getAoiAdvice API wrapper
KSvend61feaac -
feat: wire size toggles and click-to-place in app.js
KSvendb30fbad -
feat: rewrite map.js with click-to-place AOI (no MapboxDraw)
KSvend59bad34 -
feat: replace draw/upload CSS with size toggle styles
KSvendc8318e6 -
feat: replace MapboxDraw HTML with size toggle buttons
KSvend +2: Claude, HappyGenerated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>f0f6b0f
-
fix: load job data when viewing previous analyses from history
KSvend +2: Claude, HappyHistory card clicks navigated to results without fetching job data, causing "No results data" for all previously completed analyses. Now fetches full job data (including AOI bbox for map) before showing results. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>d75edf9
-
feat: add month picker to define-area sidebar and wire up season state
KSvend +1: Claude Sonnet 4.6Adds season-start/season-end dropdowns (Jan–Dec) to the Analysis season form-group, a year-boundary wrap hint, and surfaces the selected season in the confirm summary and submit payload as season_start / season_end. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>a86413f
-
fix: show waiting status for indicators not yet started in progress view
KSvend +2: Claude, HappyThe status list only showed indicators already in the progress dict, hiding ones that hadn't started yet. Now shows all requested indicator IDs with "Waiting" badge for those not yet processing or complete. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>5e3b25e -
fix: remove MapboxDraw during rectangle tap to fix mobile touch events
KSvend +2: Claude, HappyMapboxDraw's simple_select mode intercepts click/tap events, preventing the two-tap rectangle from working on mobile. Now temporarily removes Draw control during the tap phase, re-adds it after the rectangle is built. Also adds a visual marker on first tap for feedback. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>8e098b4 -
fix: replace broken draw_rectangle with two-tap rectangle for mobile support
KSvend +2: Claude, Happydraw_rectangle is not a built-in MapboxDraw mode and no plugin was loaded, so rectangle drawing silently failed. Replaced with a two-tap approach: first tap sets one corner, second tap sets the opposite corner. Works on both touch (mobile) and click (desktop). Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>b15c51f -
feat: frontend renders spatial overlays natively in MapLibre
KSvend +2: Claude, HappyReplace PNG image overlay with native MapLibre rendering: spatialUrl() added to api.js, map.js gains renderSpatialOverlay/clearSpatialOverlay supporting points (circle layers), choropleth (fill), grid (polygon fill), and status color fallback. results.js updated to fetch JSON from /spatial/ endpoint. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>337d9e1 -
feat: improve AOI selection UX — remove polygon, add area feedback
KSvend +2: Claude, Happy- Remove polygon draw tool (always converted to bbox anyway) - Show computed area in km² as user draws - Block "Continue" if area exceeds 10,000 km² limit with clear message - Add area display styling using MERLx design tokens Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>bc4a082 -
fix: downloads and map overlays fail auth — use fetch with headers
KSvend +2: Claude, HappyDownload links were plain <a href> which don't send auth headers. Map overlay images were passed as URLs to MapLibre which also skips auth. Now all file fetches go through authenticated fetch() calls: - Downloads use blob URLs triggered via buttons - Map overlays fetched as blobs, then passed as blob URLs to MapLibre Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>f37d8a6 -
fix: Content-Type header dropped when authenticated, causing 422 on job submit
KSvend +2: Claude, HappyThe spread { ...defaults, ...opts } overwrote the merged headers with opts.headers (which only had Authorization). Now headers are built once and passed as the final value. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>73c5feb -
fix: display actual validation error messages instead of [object Object]
KSvend +2: Claude, HappyFastAPI returns validation errors as an array of objects in detail. Now extracts the msg field from each error for readable display. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>e8c214d -
fix: let Draw handle its own style.load lifecycle on basemap toggle
KSvend +2: Claude, HappyDraw internally listens for style.load and re-adds its GL layers. Our removeControl/addControl was destroying that internal listener, breaking both toggle-back and drawing on the satellite layer. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>ebcc0cb -
style: add CSS for basemap toggle button
KSvend +2: Claude, HappyGenerated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>bc75f75 -
fix: add double-click guard and improve BasemapToggle robustness
KSvend +2: Claude, Happy- Prevent toggle during style load with _switching flag - Use safer container.remove() in onRemove - Reset _currentStyle on initAoiMap re-entry Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>e5e17d3 -
feat: add basemap toggle control to AOI map (Positron/ESRI Satellite)
KSvend +2: Claude, HappyGenerated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>6a6634a -
refactor: rename MAP_STYLE to POSITRON_STYLE and add SATELLITE_STYLE constant
KSvend +2: Claude, HappyCo-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>cc94dbe -
fix: switch basemap from demo tiles to CARTO Positron
KSvend +2: Claude, HappyReplace MapLibre demo tiles (country outlines only) with CARTO Positron basemap showing roads, cities, and place labels so users can locate their programme areas. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>7773924
-
feat: add mobile responsive layout (768px breakpoint)
KSvend +2: Claude, HappyGenerated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>761d876 -
feat: add login flow, history page, session management, and nav guard
KSvend +1: Claude Opus 4.6 (1M context)Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>fcb9daa -
feat: add My analyses and Sign out links to topbars
KSvend +1: Claude Opus 4.6 (1M context)Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>6fc693d -
feat: add history page HTML and CSS
KSvend +1: Claude Opus 4.6 (1M context)Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>b5c28d8 -
feat: add login page HTML and CSS
KSvend +1: Claude Opus 4.6 (1M context)Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>f49c9d9 -
feat: add auth header to API calls, add login and listJobs API functions
KSvend +1: Claude Opus 4.6 (1M context)Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>4f2f97a
-
feat: fix nightlights with real VIIRS data + placeholder data warnings
KSvend +2: Claude, HappyNightlights indicator now reads real radiance from Planetary Computer HREA light-composite COGs (no auth), with NASA Black Marble VNP46A4 as a second source (free Earthdata creds), falling back to synthetic. All 9 indicators now report whether they used real satellite data or placeholder values via a new data_source field on IndicatorResult. The frontend shows an amber "Placeholder data" badge on result cards, and the PDF report includes a per-indicator warning when placeholder data was used. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>619edca -
feat: wire up indicator map layers on results dashboard
KSvend +2: Claude, HappySpatial indicators (cropland, vegetation, water) now cache their 2D raster data after processing. The worker generates styled PNG maps via render_indicator_map(), served through a new API endpoint. The frontend overlays these on the results map when clicking indicator cards. - Add SpatialData dataclass and get_spatial_data() to BaseIndicator - Cache NDVI/MNDWI grids in cropland, vegetation, water indicators - Worker generates map PNGs alongside charts during job processing - New GET /api/jobs/{job_id}/maps/{indicator_id} endpoint - Frontend: MapLibre image overlay with graceful fallback for non-spatial indicators - Fix setuptools package discovery in pyproject.toml Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>85f7c19 -
rebrand: apply full MERLx Aperture identity per design spec
KSvend +2: Claude, HappyUpdates all user-facing copy (landing page, PDF reports, emails, README) and backend metadata to use "MERLx Aperture" branding consistently, per the approved 2026-03-26 rebrand design specification. Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthropic.com> Co-Authored-By: Happy <yesreply@happy.engineering>73b5679 -
Initial commit: Aperture platform (extracted from SR4S)
KSvendae74af5