Metadata-Version: 2.4
Name: geoaquacrop_visualize
Version: 0.1.0
Summary: Interactive Dash/Plotly visualisation toolkit for gridded AquaCrop simulations
Author: Josias Lang-Ritter, Chris Bowden, Seyed Hossein Hosseini, Henrikki Tenkanen, Tim Foster
Maintainer-email: Seyed Hossein Hosseini <seyed.h.hosseini@aalto.fi>
License-Expression: Apache-2.0
Project-URL: Documentation, https://geoaquacrop-visualize.readthedocs.io/en/stable/
Project-URL: Repository, https://github.com/aquacropos/geoaquacrop_visualize
Project-URL: Preprocessing, https://github.com/aquacropos/geoaquacrop_preprocess
Project-URL: Simulation, https://github.com/aquacropos/geoaquacrop_simulate
Keywords: aquacrop,agronomy,crop-model,hydrology,geospatial,dash,plotly,visualization
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Web Environment
Classifier: Framework :: Dash
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
Classifier: Topic :: Scientific/Engineering :: Hydrology
Classifier: Topic :: Scientific/Engineering :: Visualization
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pandas
Requires-Dist: numpy
Requires-Dist: xarray
Requires-Dist: netCDF4
Requires-Dist: scipy
Requires-Dist: plotly>=6
Requires-Dist: dash
Requires-Dist: dash-bootstrap-components
Provides-Extra: geotiff
Requires-Dist: rasterio; extra == "geotiff"
Provides-Extra: test
Requires-Dist: pytest; extra == "test"
Provides-Extra: docs
Requires-Dist: sphinx>=7; extra == "docs"
Requires-Dist: furo; extra == "docs"
Provides-Extra: dev
Requires-Dist: geoaquacrop_visualize[docs,geotiff,test]; extra == "dev"
Dynamic: license-file

# GeoAquaCrop Visualize

An interactive Dash/Plotly application for exploring **gridded [AquaCrop-OSPy](https://github.com/aquacropos/aquacrop)
simulations**. Point it at a completed model run and it gives you a map of the
region, a variable picker, and a click-through to per-cell daily time series —
plus the inputs those runs were driven by, and a gridded export of anything you
select.

- [Requirements](#requirements)
- [Installation](#installation)
- [Connecting your data](#connecting-your-data)
- [Running the app](#running-the-app)
- [Using the app](#using-the-app)
- [Exporting data](#exporting-data)
- [Configuration](#configuration)
- [Project structure](#project-structure)
- [Running the tests](#running-the-tests)
- [Documentation](#documentation)
- [Troubleshooting](#troubleshooting)

---

## What you get

| | |
|---|---|
| **Choropleth maps** | 11 seasonal output variables (yield, production, irrigation, ET, water productivity) over the simulation grid, on an [OpenFreeMap](https://openfreemap.org) Positron basemap with the region outline traced on top. |
| **Daily time series** | 13 daily variables (water fluxes, soil water, crop development) for any cell, read straight from the simulation's daily tables. |
| **Model inputs** | The climate forcing (4 variables), the crop calendar, and SPAM physical crop areas, on the same grid as the outputs. |
| **Spatial selection** | Click a cell for its time series; lasso or box-select many cells to highlight them and scope an export to just that area. |
| **Season handling** | One season at a time, or *All years* aggregated by mean or sum — each variable carries a sensible default. |
| **Gridded export** | Any set of cells, variables, and dates to NetCDF, GeoTIFF, or CSV. |

Everything loads once at startup, so interaction is instant afterwards.

---

## Requirements

- **Python 3.11 or newer**
- **A completed AquaCrop run.** The app is a viewer, not a model — it reads the
  outputs of `geoaquacrop-simulate` and the preprocessed inputs from
  `geoaquacrop-preprocess`. See [Connecting your data](#connecting-your-data).

Runtime dependencies (`pandas`, `numpy`, `xarray`, `netCDF4`, `scipy`,
`plotly`, `dash`, `dash-bootstrap-components`) install automatically.

---

## Installation

```bash
pip install "geoaquacrop_visualize[geotiff]"
```

From a checkout, use `pip install -e ".[geotiff]"` instead. The project uses a
`src` layout, so the package is importable only once it is installed.

| Extra | Adds | For |
|---|---|---|
| `geotiff` | `rasterio` | GeoTIFF export. Without it, that one format is skipped and the other two still work. |
| `test` | `pytest` | The test suite. |
| `docs` | `Sphinx`, `sphinx-rtd-theme` | Building the documentation. |
| `dev` | all of the above | Contributing. |

Dependencies are declared without version constraints, so pip resolves whatever
is current. If you need a reproducible environment, capture one with
`pip freeze > requirements.txt` after a working install.

---

## Connecting your data

By default the app looks for its data in the folder you launch it from, or in
the workspace root given by `--root` / `$GEOAQUACROP_ROOT`:

```
<working directory>/                  # or $GEOAQUACROP_ROOT
├── outputs/                          # $GEOAQUACROP_OUTPUTS
│   ├── summary_results_<timestamp>.pkl    # seasonal results per cell
│   └── daily_results_<timestamp>.pkl      # daily tables per cell
├── processed/                        # $GEOAQUACROP_PROCESSED
│   ├── MaxTemp*.nc  MinTemp*.nc  Precipitation*.nc  ReferenceET*.nc
│   ├── cropcalendar.nc
│   └── spam*_physical_area.nc
└── region.geojson                    # $GEOAQUACROP_REGION — region outline
```

**Data somewhere else.** Pass the locations as options (`--root`, `--outputs`,
`--processed`, `--region`, `--exports`), as the matching keyword arguments of
`run()`, or set the environment variables:

```bash
export GEOAQUACROP_OUTPUTS=/path/to/geoaquacrop-simulate/outputs
export GEOAQUACROP_PROCESSED=/path/to/geoaquacrop-preprocess/processed
export GEOAQUACROP_REGION=/path/to/geoaquacrop-preprocess/inputdata/region.geojson
```

**Choosing a run.** If `outputs/` holds several runs, the newest
`summary_results_*.pkl` and `daily_results_*.pkl` (by filename) are loaded.
To view a different run, point `GEOAQUACROP_OUTPUTS` at a folder that holds it.

> No data is bundled with this repository, and none needs to be copied into it.
> The region outline, for instance, is read from `GEOJSON_PATH` and thinned in
> memory at startup — see [`boundary.py`](src/geoaquacrop_visualize/boundary.py).

---

## Running the app

From the command line:

```bash
geoaquacrop_visualize --root /path/to/workspace     # console script
```

Run `geoaquacrop_visualize --help` for every option, including `--port` and
`--debug`.

From Python:

```python
import geoaquacrop_visualize as visualize

visualize.run(outputs="/path/to/outputs", port=8050)
```

Then open **<http://localhost:8050>**.

Startup loads every dataset, pre-computes the aggregations, and prints a couple
of diagnostic lines (the auto-derived map zoom, and the region boundary
statistics). Expect a few seconds, depending on the size of your run.

**Deploying behind a WSGI server.** `build_app()` assembles the app without
starting a server:

```python
from geoaquacrop_visualize import build_app

server = build_app().server      # WSGI entry point for gunicorn etc.
```

---

## Using the app

The window is a control sidebar on the left and a map canvas on the right. Two
tabs at the top of the sidebar switch what you are looking at.

### Simulation Outputs tab

1. **Choose a crop and irrigation type**, then a **season** — a single harvest
   year, or *All years*.
2. When *All years* is selected, an **Aggregation** toggle appears: *Mean* or
   *Sum*. Each variable has a sensible default (yields average, irrigation and
   production sum).
3. **Pick a map variable** from the accordion, grouped as *Yield & Production*,
   *Water Balance*, and *Water Productivity*. The map recolours immediately.
4. **Click any cell** to open its daily time series below the map. Choose the
   daily variable from the *Water Fluxes*, *Soil Water*, and *Crop Development*
   groups.
5. **Click two points on the time series** to mark a window: the app shades it,
   draws the mean as a dashed line, and reports the mean ± standard deviation
   over that period. A third click clears it.
6. **Lasso or box-select** a group of cells (the tools are in the Plotly toolbar
   at the top-right of the map) to highlight them in orange. The count appears
   in the ribbon above the map, and the export dialog can then be scoped to just
   that selection.

### Inputs tab

Shows the data that drove the simulation, on the same grid: the climate variable
of your choice (max/min temperature, precipitation, reference ET), the crop
calendar for the selected cell, and SPAM physical crop areas for the selected
crop. Clicking a cell here opens the climate time series for that cell.

### Map controls

Pan and zoom are preserved as you change variables, so you can stay zoomed into
one area while cycling through the outputs. Hovering a cell shows its
coordinates and the current variable's value.

---

## Exporting data

**⬇ Export Data…** at the bottom of the sidebar opens a dialog with four choices:

| Choice | Options |
|---|---|
| **Variables** | Any of the 13 daily variables, grouped as in the sidebar. |
| **Period** | The whole simulation, or an explicit start and end date. |
| **Cells** | The whole area, or just your current map selection. |
| **Format** | NetCDF (`.nc`), GeoTIFF (`.tif`), CSV (`.csv`) — any combination. |

Output is assembled as a time × y × x grid per variable and written to
`<root>/outputs/exports/` (override with `--exports` or `$GEOAQUACROP_EXPORTS`). The dialog reports the filenames it wrote.

GeoTIFF needs `rasterio`. If it is missing, that format is skipped with a note
in the status message and the others are still written.

---

## Configuration

All user-facing settings live in
[`src/geoaquacrop_visualize/config.py`](src/geoaquacrop_visualize/config.py).

| Setting | Default | What it does |
|---|---|---|
| `ROOT` | `$GEOAQUACROP_ROOT` or the working directory | Workspace root; the locations below default to paths under it. |
| `OUTPUTS_DIR` | `$GEOAQUACROP_OUTPUTS` or `outputs` | Folder holding the simulation result pickles. |
| `PROCESSED_DIR` | `$GEOAQUACROP_PROCESSED` or `processed` | Climate, crop calendar, and SPAM grids. |
| `GEOJSON_PATH` | `$GEOAQUACROP_REGION` or `region.geojson` | The region outline. |
| `CELL_RES` | `$GEOAQUACROP_CELL_RES` or `0.05` | Grid cell size in degrees. Must match the preprocessing grid. |
| `SUMMARY_PKL`, `DAILY_PKL` | newest match in `OUTPUTS_DIR` | Which simulation run to display. |
| `EXPORT_DIR` | `$GEOAQUACROP_EXPORTS` or `outputs/exports` | Where exports are written. |
| `PORT` | `$GEOAQUACROP_PORT` or `8050` | Port the app serves on. |
| `MAP_HEIGHT`, `TS_HEIGHT` | `550`, `400` | Canvas heights in pixels. |
| `BOUNDARY_SIMPLIFY_EPS` | `0.004` | Outline simplification tolerance in degrees (~400 m). Raise for a lighter outline, lower for a crisper one. |
| `MAP_VARIABLES` | 11 entries | Map variable catalogue: label, colourscale, default aggregation. |
| `DAILY_VARIABLES` | 13 entries | Daily variable catalogue: label, source table, line colour. |
| `CLIMATE_VARIABLES` | 4 entries | Climate variable catalogue. |

Adding a variable is a matter of adding an entry to the relevant catalogue — the
sidebar, the maps, and the export dialog all read from these dictionaries.

---

## Project structure

```
geoaquacrop_visualize/
├── pyproject.toml                  # Packaging + pytest configuration
├── src/
│   └── geoaquacrop_visualize/      # Application package, 22 modules
│       ├── __init__.py             #   ← run() / build_app(), console script entry
│       ├── config.py               #   ← paths, sizes, variable catalogues
│       ├── utils.py  data.py  grid.py  aggregates.py  queries.py
│       ├── boundary.py             #   ← region outline, thinned and served by URL
│       ├── export.py  figures_maps.py  figures_timeseries.py
│       ├── styles.py  app_shell.py
│       ├── layout*.py              #   ← sidebar, panels, export modal, assembly
│       └── callbacks_*.py          #   ← controls, selection, maps, ts, export
├── tests/                          # One test module per application module
└── docs/                           # Sphinx sources (Read the Docs)
```

Importing the package loads no data; the datasets are read and the app is
assembled only when `run()` or `build_app()` is called.

The package is split by concern, and the split is enforced: the test suite
checks that no module exceeds 500 lines, that the import graph stays acyclic,
that callbacks never import each other, and that only `data.py` opens the
pickles — so the datasets can only ever be loaded once.

---

## Running the tests

```bash
pip install -e ".[test]"
pytest                            # whole suite
pytest tests/test_utils.py -v     # a single module
pytest -k colorscale              # by keyword
```

No data files are required to run the tests.

---

## Documentation

Full documentation — architecture, API reference, and configuration — is built
with Sphinx from `docs/` and hosted on Read the Docs. To build it locally:

```bash
pip install -e ".[docs]"
sphinx-build -b html docs docs/_build/html
```

---

## Troubleshooting

**`FileNotFoundError` at startup**
A data file was not found where `config.py` expects it. The error names the
missing path. Either launch the app from the folder holding `outputs/`,
`processed/`, and `region.geojson`, pass `--root`, or point at each location
with `--outputs`, `--processed`, and `--region` (or the matching
`GEOAQUACROP_*` variables).

**The wrong run is displayed**
The newest `summary_results_*.pkl` and `daily_results_*.pkl` in
`GEOAQUACROP_OUTPUTS` are loaded. Make sure both come from the same run, or
point `GEOAQUACROP_OUTPUTS` at a folder holding only the run you want.

**No region outline on the map**
The outline is served from `/region-boundary.geojson`, derived at startup from
`GEOJSON_PATH`. Startup prints a `Region boundary: …` line reporting how many
polygons and vertices it produced — if that line is missing or reports zero, the
source GeoJSON is the place to look.

**`(rasterio not installed — GeoTIFF skipped)` in the export status**
Install the optional extra: `pip install "geoaquacrop_visualize[geotiff]"`.

**The map is blank but the sidebar works**
The basemap tiles come from OpenFreeMap's public tile server, so the app needs network
access to draw them. The data layers render regardless.

---

## Acknowledgements

The basemap uses the Positron style from [OpenFreeMap](https://openfreemap.org).
Map data: [© OpenMapTiles](https://www.openmaptiles.org/), data from [OpenStreetMap](https://www.openstreetmap.org/copyright).
