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 -
fix: anchor all maps on AOI bbox to stop squashed hotspot images
KSvend +1: Claude Opus 4.6 (1M context)The hotspot map was using the product raster's bounds for set_xlim/ylim, while render_raster_map was using the true-color raster's bounds. When CDSE returned a product raster with tile-boundary-cropped bounds (e.g. a 0.03° × 0.08° strip), the hotspot came out several times flatter than the top product map. Fix: - new _figsize_for_extent() sizes the matplotlib figure to match the data aspect (cos-lat corrected) so tight_layout + bbox_inches never produce a stretched PNG - render_raster_map, render_hotspot_map, and render_overview_map all now anchor xlim/ylim on the AOI bbox instead of whichever raster happened to be opened first - imshow() calls no longer pass the aspect kwarg — set_aspect() on the axes is the single source of truth Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>ccd0638 -
fix: aspect ratio, confidence factors, GREEN trend alignment, compound signal gating
KSvend +1: Claude Opus 4.6 (1M context)- maps: geographic aspect (cos-lat) replaces aspect="auto" that caused skewed rendering - report: PIL-based _fit_image preserves PNG aspect ratio in PDF layout - report: coordinate-derived AOI display name when name is missing - report: drop headline truncation in summary table - report: 4-factor confidence breakdown incl. anomaly consistency - narrative: drift/gating-aware interpretations via get_interpretation_for_result - confidence: continuous scoring, new anomaly_consistency factor, expected_months - base: status-aware trend (GREEN -> STABLE), evidence-gated classification - worker: skip GREEN and baseline-drift indicators before compound signal detection Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>b0128ec -
fix: data integrity — built-up persistence, status gating, SAR drift detection, direction-aware narrative
KSvend +1: Claude Opus 4.6 (1M context)Second-pass fixes based on the Nyala South (Sudan) test report showing AMBER flags driven by statistical artifacts rather than real signals. 1. Built-up areas: persistence-based classification - Replace monthly NDBI>0 classification with a persistence mask: pixel is built-up only if NDBI>0 AND NDVI<0.15 in ≥60% of months across the analysis period. Removes the seasonal cycling artifact where bare soil got classified as built-up in dry seasons only. - Tighten NDVI threshold from 0.2 → 0.15 (sparse-veg regions). - Replace monthly z-score with direct percentage change between current and baseline persistent masks. Change thresholds: |change|≥15% → RED, ≥5% → AMBER, else GREEN. - Chart now shows baseline vs current as two bars instead of an oscillating monthly time series that misrepresents reality. - Shared _analyze_persistence helper used by both batch and non-batch harvest paths (eliminates duplication). - Smoke-tested: synthetic sea…ffb57c8 -
fix: report contradictions, NaN propagation, NDBI threshold, blue tint
KSvend +1: Claude Opus 4.6 (1M context)Five fixes for the v2 analytical report based on the test PDF review: 1. SAR NaN propagation - Add safe_float helper that converts NaN/inf to 0.0 - Apply at every np.nanmean/nanstd source (sar, ndvi, water, buildup) - Prevents 'nan dB' / 'z=+nan' strings in headlines and chart data 2. Headline/status alignment - Add BaseProduct._generate_headline as single source of truth - Headlines for RED/AMBER never say 'within normal range'; they describe whichever signal (z-score, hotspot %, anomaly months) drove the non-GREEN status - All four products use the unified generator - Fix _build_seasonal_chart_data to use a year-aware date helper (was reusing time_range.end.year for every month) - Buildup chart had unit mismatch (current ha vs raw NDBI ha); now uses paired baseline buildup fractions for envelope 3. Settlement NDBI threshold for arid terrain - openEO graph now emits paired NDBI/NDVI bands per timestep - _compute_stats applies combin…e968fb9
-
fix: sanitize NaN/inf floats to prevent JSON serialization crashes
KSvend +1: Claude Opus 4.6 (1M context)SAR (and potentially other EO products) produce NaN/inf from nanmean on empty pixel slices. These values crash json.dumps() in both the database write path and the API response path. Adds sanitize_for_json() utility that recursively replaces NaN/inf with 0.0, applied at three levels: - ProductResult model_validator (catches on construction) - Database save_job_result (catches on storage) - API get_job endpoint (catches on response) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>2850a7e -
fix: accept legacy indicator_id/indicator_ids from old database records
KSvend +1: Claude Opus 4.6 (1M context)The rename to product_id/product_ids broke deserialization of existing jobs stored in SQLite. Add model_validators to accept both old and new field names. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>09fc02d -
fix: merge multiple TIF files from CDSE batch downloads
KSvend +1: Claude Opus 4.6 (1M context)CDSE may return temporal bands as separate TIF files instead of a single multi-band GeoTIFF. The _find_tif method now detects this and merges them. Also adds diagnostic logging for band count, CRS, and file listing to help debug the 1-composite issue. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>7e20cf0 -
fix: always reproject to EPSG:4326 in openEO graph builders
KSvend +1: Claude Opus 4.6 (1M context)CDSE returns data in native UTM projection when resample_spatial is skipped. This breaks aggregate_temporal_period (returns 1 band instead of 12) and causes maps to show projected coordinates instead of lat/lon. Fix: always call resample_spatial with EPSG:4326 at the requested resolution, even at native 10m/20m. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>42ff8a1
-
fix: fix remaining indicator_id/indicator_ids references after rename
KSvend +2: Claude, HappySeveral references to old field names survived the rename: - job.request.indicator_ids → product_ids (jobs.py, worker.py) - result.indicator_id → product_id (worker.py, 15 occurrences) - indicator_name kwarg → product_name (worker.py) - indicator_status param → status (database.py) - advisor prompt text cleanup These caused AttributeError at runtime on HF Spaces. 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>749b346 -
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: add anthropic and rasterio to Dockerfile
KSvend +2: Claude, HappyBoth packages are in pyproject.toml but were missing from the Dockerfile's explicit pip install. The --no-deps flag on the app install means they were never present in the Docker image. 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>70fbdf4 -
chore: remove stale RESOLUTION_M constant
KSvend +2: Claude, HappyNo longer used — all indicators now have per-indicator native resolution constants (NDVI_RESOLUTION_M, etc.). 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>f954d8c -
Merge pull request #2 from KSvend/feat/eo-product-overhaul
KSvendUnverified signaturefeat: EO product analytical upgradecf3df79 -
fix: address code review — memory, dedup, confidence, zoom, overview
KSvend +2: Claude, HappyCritical fixes: - Memory: compute_seasonal_stats_pixel uses Welford's online algorithm instead of stacking all bands in RAM (prevents OOM at native res) - Memory: worker releases z-score rasters from indicators after compound signal detection via release_rasters() Important fixes: - Extract _compute_spatial_completeness, _classify_zscore, _compute_trend_zscore to BaseIndicator (was copy-pasted 4x) - Remove fake mean_obs_per_composite=5.0 — confidence model is now 3-factor (temporal, baseline depth, spatial completeness) - overview.py factors anomaly_months into composite score penalty - Worker zoom: order=1 (bilinear) and upsample to finest resolution instead of downsampling to coarsest - Water chart: compute baseline from water fractions directly instead of misleadingly scaling raw MNDWI values - Buildup _compute_stats docstring consistency Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-Authored-By: Claude <noreply@anthrop…7daf007 -
feat: add output pipeline for seasonal analysis and compound signals
KSvend +2: Claude, Happy- Charts: anomaly markers (red rings) on months exceeding z-score threshold; default y-axis labels per indicator - Maps: new render_hotspot_map() for z-score change visualization over true-color base layer - Narrative: generate_compound_signals_text() for cross-indicator compound signal descriptions - Report: anomaly column in summary table, compound signals section, hotspot maps in indicator blocks, confidence breakdown table in technical annex - Worker: generates hotspot maps, detects compound signals via spatial z-score overlap, passes both to PDF report 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>1e8f4b0 -
feat: upgrade Water, SAR, and Settlement indicators with seasonal analysis
KSvend +2: Claude, HappyApply the same analytical upgrade pattern from NDVI to all three remaining batch indicators: - Seasonal baselines via compute_seasonal_stats_aoi - Per-month z-score anomaly detection - Pixel-level hotspot detection with compute_zscore_raster - Four-factor confidence scoring - Native resolution (Water 20m, SAR 10m, Settlement 20m) - Seasonal envelope chart data with anomaly flags SAR uses custom seasonal stats from VV means (interleaved VV/VH bands can't go through compute_seasonal_stats_aoi directly). 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>3d48a34 -
chore: remove NDVI harvest diagnostic print statements
KSvend +2: Claude, HappyPipeline confirmed stable — remove debug prints added during batch job troubleshooting. 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>9d401d9 -
feat: add cross-indicator compound signal detection
KSvend +1: Claude Opus 4.6 (1M context)Implements compound signal module with 4 detection patterns: - land_conversion: NDVI decline + settlement growth - flood_event: SAR backscatter decrease + water increase - drought_stress: NDVI decline + water decline + SAR increase - displacement_pressure: settlement growth adjacent to vegetation loss Includes overlap calculation, confidence tagging, and comprehensive tests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>ae4c60c -
feat: add four-factor confidence scoring model
KSvendcbe084f -
feat: add pixel-level change detection with z-scores and hotspot clustering
KSvend +1: Claude Opus 4.6 (1M context)Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>aa09e0b -
feat: add seasonal baseline computation module
KSvend +1: Claude Opus 4.6 (1M context)Implements core seasonal baseline functionality for EO anomaly detection: - group_bands_by_calendar_month: Maps multi-year band indices to calendar months - compute_seasonal_stats_aoi: Computes per-month AOI-level stats from baseline raster - compute_seasonal_stats_pixel: Computes per-pixel statistics across bands - compute_zscore: Computes z-scores with std-floor to avoid division-by-zero All 6 tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>03db879 -
feat: update openEO graph builders to native resolution (10-20m)
KSvend515a08f -
feat: add per-indicator native resolution and z-score threshold config
KSvendbfe3c94 -
feat: add anomaly, hotspot, confidence fields to IndicatorResult; add CompoundSignal model
KSvend87be95e -
feat: add test fixtures for EO product overhaul
KSvend46ca87a -
Add implementation plan for EO product analytical upgrade
KSvend +1: Claude Opus 4.6 (1M context)19 tasks covering: data model changes, native resolution, seasonal baselines, pixel-level change detection, compound signals, confidence model, chart/map/narrative/report improvements. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>30f032f -
Add EO product analytical upgrade design spec
KSvend +1: Claude Opus 4.6 (1M context)Covers native resolution, seasonal baselines, pixel-level change detection, cross-indicator correlation, confidence model overhaul, and report/visualization improvements for all 4 indicators. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>804eb65 -
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: use MAX_AOI_KM2 from config instead of hardcoded 10,000 km² in models
KSvend +2: Claude, HappyBackend now enforces the same 500 km² limit shown in config.py. Previously models.py had its own MAX_AREA_KM2 = 10,000 constant that was never connected to the configurable 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>31cdc2e -
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: add /api/aoi-advice endpoint and startup logging
KSvend4f52b3e -
feat: add advisor module — Claude-powered region insight
KSvend9f90242 -
feat: add ANTHROPIC_API_KEY config and AoiAdviceRequest model
KSvend7534962 -
chore: add anthropic SDK dependency
KSvendbd83d04 -
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 -
docs: implementation plan for AOI advisor (Claude-powered region insight)
KSvend +2: Claude, Happy8 tasks: dependency, config/model, advisor module, API endpoint, frontend API wrapper, HTML/CSS card, app.js wiring, startup logging. 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>b595465 -
docs: spec for AOI advisor — Claude-powered region insight
KSvend +2: Claude, HappyLLM-driven advice card that recommends timeframe and indicator priorities when user places an AOI. Uses Claude Haiku via backend proxy. 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>12eb573 -
docs: implementation plan for AOI click-to-place redesign
KSvend +2: Claude, Happy5 tasks: HTML cleanup, CSS styles, map.js rewrite, app.js wiring, and limit verification. 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>27ce485 -
docs: spec for AOI click-to-place redesign
KSvend +2: Claude, HappySingle-click placement with preset sizes (100/250/500 km²), replacing MapboxDraw two-tap rectangle and GeoJSON upload. 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>504144a -
refactor: strip to 4 real EO indicators, remove all tests and mock fallbacks
KSvend +2: Claude, HappyKeep only NDVI, Water (MNDWI), SAR, and Built-up — the four indicators with full openEO batch pipelines. Remove 8 unused indicators, all test files, and every placeholder/fallback path. Failures now propagate as errors instead of silently returning synthetic data. - Delete: cropland, fires, food_security, lst, nightlights, no2, rainfall, vegetation - Delete: entire tests/ directory - Remove _fallback() from all indicators — process() raises on failure - Remove harvest() fallback-to-placeholder on current data failure - Worker no longer falls back to process() when batch submit fails - Remove placeholder caveat from narrative engine and PDF report - Re-weight overview scores for 4 indicators (ndvi 0.30, sar 0.25, water 0.25, buildup 0.20) - Remove build_lst_graph from openeo_client 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>c42be1f