Metadata-Version: 2.4
Name: aemetxfb
Version: 0.1.0
Summary: Access weather and climate data from AEMet (Spanish Meteorological Agency) without an API key
Project-URL: Homepage, https://codeberg.org/kikocorreoso/aemetxfb
Project-URL: Repository, https://codeberg.org/kikocorreoso/aemetxfb
Project-URL: Issues, https://codeberg.org/kikocorreoso/aemetxfb/issues
Project-URL: Documentation, https://kikocorreoso.codeberg.page/aemetxfb
Author: kikocorreoso
License-Expression: GPL-3.0-only
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.12
Requires-Dist: numpy>=2.4.3
Requires-Dist: pandas>=3.0.1
Provides-Extra: dev
Requires-Dist: ipython>=9.11.0; extra == 'dev'
Requires-Dist: matplotlib>=3.10.8; extra == 'dev'
Provides-Extra: docs
Requires-Dist: sphinx-rtd-theme>=3.1.0; extra == 'docs'
Requires-Dist: sphinx>=9.1.0; extra == 'docs'
Provides-Extra: tests
Requires-Dist: pytest>=9.0.2; extra == 'tests'
Requires-Dist: ruff>=0.15.7; extra == 'tests'
Description-Content-Type: text/markdown

[Spanish version](README.md)

# aemetxfb

Python package to access AEMet data without API key

## Documentation

To build the documentation locally:

```bash
# Install Sphinx dependencies
uv run pip install -e ".[docs]"

# Build HTML documentation
./scripts/build_docs.sh
```

The documentation will be generated in `docs/build/html/`. Open `index.html` in your browser to view it.

## API Reference

### `aemetxfb.clim` — Climate data

Climate-related data including station information, ephemerides, extremes, normals, and threshold exceedances.

#### Station data

| Item | Type | Description |
|------|------|-------------|
| `STATIONS` | `dict[str, dict]` | Static dictionary with ~80 climate stations. Each entry contains `lat`, `lon`, `alt`, `id`, `est` (name), and `period`. Source: AEMet GeoJSON. |
| `CLIM_EXTREMES_STATIONS` | `dict[str, str]` | Static dictionary with ~120 stations available for climate extremes data. Maps station ID → station name. |

#### `get_clim_ephem(day=0, month=0, year=0, keyword="")` → `dict[str, Any]`

Fetches meteorological ephemerides and commemorations (historical weather events).

- **Returns:** A dictionary with keys `"search_params"`, `"Efemérides"` (list of `(date, description)` tuples), and `"Conmemoraciones"` (list of `(date, description)` tuples).
- **Data source:** CSV from AEMet API.
- At least one parameter must be provided (non-default value).

#### `get_clim_extremes(loc, when="year")` → `pd.DataFrame`

Fetches absolute extreme climate values (max/min since 1920) for a station.

- **Returns:** A `pandas.DataFrame` with variable names as index and columns `value` (float) and `timestamp` (str, e.g. `"1963/12/29"`). The DataFrame has a `name` attribute with the station name.
- **Data source:** CSV from AEMet API.
- Variables include: max/min temperature, max precipitation, max wind gust, etc.

#### `get_normals(loc)` → `pd.DataFrame`

Fetches climatological normal values for a station.

- **Returns:** A `pandas.DataFrame` with month names in English (Jan–Dec + `Year`) as index and columns `T`, `TM`, `Tm`, `R`, `H`, `DR`, `DN`, `DT`, `DF`, `DH`, `DD`, `I` (all numeric). The DataFrame has a `name` attribute with the station name.
- **Data source:** CSV from AEMet API.

#### `get_normals_map(output_path)` → `str`

Downloads climate normals maps for Spain (peninsular, Balearic, and Canary Islands).

- **Returns:** The path to the downloaded file as a string.
- **Downloads:** A `.tar.gz` file at `output_path` containing map images for variables like precipitation, temperature, Köppen classification, snow days, and sunshine hours.

#### `get_clim_threshold_day(date_input, param)` → `pd.DataFrame`

Fetches stations where a threshold was exceeded on a given day.

- **Returns:** A `pandas.DataFrame` with columns `nombre_est`, `i_c`, `altitud`, the parameter column (`pptSup40` or `vtoSup*` with values 1.0/0.0/NaN), `lon`, and `lat`. Returns an empty DataFrame with `lon`/`lat` columns if no stations exceed the threshold.
- **Data source:** GeoJSON from AEMet API.
- Valid `param` values: `"pptSup40"`, `"vtoSup70"`, `"vtoSup80"`, `"vtoSup90"`, `"vtoSup96"`.

#### `get_clim_threshold_month(date_input)` → `pd.DataFrame`

Fetches monthly threshold exceedance counts for all stations.

- **Returns:** A `pandas.DataFrame` with columns `nombre_est`, `i_c`, `pptSup40`, `vtoSup70`, `vtoSup80`, `vtoSup90`, `vtoSup96` (numeric counts of days). Returns an empty DataFrame if no data.
- **Data source:** JSON from AEMet API.

---

### `aemetxfb.obs` — Observation data

Real-time and recent observation data including chemical composition, lightning, station measurements, radar, radiation, and satellite imagery.

#### `obs.chem` — Atmospheric chemical composition

| Function | Returns | Description |
|----------|---------|-------------|
| `get_chem_today(output_path, loc, prod)` | `str` | Downloads a `.gif` image of current chemical data (ozone, NOx, SO₂, solar radiation) for a location. Returns the output path. |
| `get_chem_previous_day(output_path, loc, prod)` | `str` | Downloads a `.gif` image of previous day's chemical data. Returns the output path. |
| `get_chem_previous_month(output_path, loc)` | `str` | Downloads a `.gif` image of previous month's chemical evolution (ozone + NO₂ only). Returns the output path. |

#### `obs.lightning` — Lightning data

| Function | Returns | Description |
|----------|---------|-------------|
| `get_lightning_latest(output_path)` | `None` | Downloads a `.tar.gz` file (containing Geotiff images) of cloud-to-ground lightning discharges for the last 24 hours. The file is saved at `output_path`. |

#### `obs.masts` — Station observations

| Item | Type | Description |
|------|------|-------------|
| `met_masts` | `tuple[str, str]` | Static tuple with ~700 station `(id, name)` pairs for observation data. |

| Function | Returns | Description |
|----------|---------|-------------|
| `get_last_24h(station_id)` | `pd.DataFrame` | Returns hourly weather data for the last 24 hours. Columns include datetime, temperature, wind speed/direction, humidity, pressure, etc. (numeric and string types). |
| `get_daily_summary(station_id)` | `pd.DataFrame` | Returns the current day's summary with max/min temperatures, wind, precipitation, etc. Each extreme value has a companion `"(time)"` column indicating when it occurred. |
| `get_previous_daily_summaries(station_id)` | `pd.DataFrame` | Returns historical daily summaries with date column, daily extremes, and precipitation accumulations. |

#### `obs.radar` — Radar imagery

| Item | Type | Description |
|------|------|-------------|
| `regional_radars` | `tuple[str]` | Tuple of 17 regional radar identifiers (e.g. `"COR"`, `"GLD"`, `"TJV"`). |
| `products` | `tuple[str]` | Tuple of 4 available radar products (`PPI.Z_005_240`, `TOP.12DBZ_240`, `RN1.1HR_CAPPI`, `RNN.6HR_CAPPI`). |

| Function | Returns | Description |
|----------|---------|-------------|
| `get_radar_PI_IB_refl_4h(output_path)` | `None` | Downloads a `.tar.gz` file with composite reflectivity images (Iberian Peninsula + Balearic Islands, last 4h, Geotiff). Saved at `output_path`. |
| `get_radar_regional_latest(output_path)` | `None` | Downloads a `.tar.gz` file with the latest regional radar images (reflectivity, echotop, precipitation, Geotiff). Saved at `output_path`. |
| `get_radar_regional_reflectivity_4h(radar, output_path, period=0, prefix="")` | `dict[str, list[str]]` | Downloads 1–25 `.png` reflectivity images (10-min intervals, last 4h) + matching `.json` bounding box files. Returns `{"png": [paths], "json": [paths]}`. |
| `get_radar_regional_echotop_4h(radar, output_path, period=0, prefix="")` | `dict[str, list[str]]` | Downloads 1–25 `.png` echotop images (10-min intervals, last 4h) + matching `.json` bounding box files. Returns `{"png": [paths], "json": [paths]}`. |
| `get_radar_regional_accumprec1_24h(radar, output_path, period=0, prefix="")` | `dict[str, list[str]]` | Downloads 1–25 `.png` 1h accumulated precipitation images (hourly, last 24h) + matching `.json` bounding box files. Returns `{"png": [paths], "json": [paths]}`. |
| `get_radar_regional_accumprec6_36h(radar, output_path, period=0, prefix="")` | `dict[str, list[str]]` | Downloads 1–7 `.png` 6h accumulated precipitation images (6-hourly, last 36h) + matching `.json` bounding box files. Returns `{"png": [paths], "json": [paths]}`. |
| `get_radar_PI_IB_refl_24h(output_path, period=0, prefix="")` | `dict[str, list[str]]` | Downloads 1–145 `.png` composite reflectivity images (10-min intervals, last 24h, Iberian Peninsula + Balearic Islands) + matching `.json` bounding box files. Returns `{"png": [paths], "json": [paths]}`. |

#### `obs.radiation` — Radiation, UVI, and ozone

| Item | Type | Description |
|------|------|-------------|
| `rad_stations` | `tuple[str]` | 26 stations with full radiation data. |
| `ir_stations` | `tuple[str]` | 19 stations with infrared radiation data. |
| `ozone_stations` | `tuple[str]` | 7 stations with ozone data. |
| `ozone_sounding_stations` | `tuple[str]` | 2 stations with ozone sounding data (`"BarajasMad"`, `"BotanicoTfe"`). |

| Function | Returns | Description |
|----------|---------|-------------|
| `get_radiation_rad(output_path, loc, prefix="")` | `str` | Downloads a `.png` image of solar radiation (global/direct/diffuse). Returns the file path. |
| `get_radiation_ir(output_path, loc, prefix="")` | `str` | Downloads a `.png` image of infrared radiation. Returns the file path. |
| `get_UVI_previous_day_img(output_path, loc, prefix="")` | `str` | Downloads a `.png` image of UVI for the previous day. Returns the file path. |
| `get_UVI_previous_day()` | `pd.DataFrame` | Returns hourly UVI values (columns `07`–`22` + `MAX`) for all stations. Parsed from HTML table. |
| `get_UVI_running_year_img(output_path, loc, prefix="")` | `str` | Downloads a `.png` image of UVI for the running year. Returns the file path. |
| `get_ozone_running_year(output_path, loc)` | `str` | Downloads a `.png` image of ozone for the running year. Returns the file path. |
| `get_ozone_sounding(output_path, loc)` | `str` | Downloads a `.png` image of ozone sounding profile. Returns the file path. |
| `get_ozone_previous_day()` | `pd.DataFrame` | Returns ozone values (column `"Ozono (UD)"`) for all stations. Parsed from HTML table. |

#### `obs.satellite` — Satellite imagery

| Function | Returns | Description |
|----------|---------|-------------|
| `get_satellite_IR_24h(output_path, period=0, prefix="")` | `list[str]` | Downloads 1–25 `.gif` infrared satellite images (hourly, last 24h). Returns list of file paths. |
| `get_satellite_VIS_24h(output_path, period=0, prefix="")` | `list[str]` | Downloads 1–25 `.jpg` visible satellite images (hourly, last 24h). Skips unavailable nighttime images. Returns list of file paths. |
| `get_satellite_global_24h(output_path, period=0, prefix="")` | `list[str]` | Downloads 1–9 `.gif` global satellite images from Meteosat/GOES/Himawari (every 3h, last 24h). Returns list of file paths. |
| `get_satellite_globe_0_24h(output_path, period=0, prefix="")` | `list[str]` | Downloads 1–9 `.gif` Earth images from Meteosat at 0° longitude (every 3h, last 24h). Returns list of file paths. |
| `get_satellite_globe_415_24h(output_path, period=0, prefix="")` | `list[str]` | Downloads 1–9 `.gif` Earth images from Meteosat at 41.5°E longitude (every 3h, last 24h). Returns list of file paths. |
| `get_satellite_airmasses_24h(output_path, period=0, prefix="")` | `list[str]` | Downloads 1–25 `.jpg` RGB air mass composite images (hourly, last 24h). Returns list of file paths. |
| `get_satellite_NDVI(output_path, prefix="")` | `str` | Downloads 1 `.gif` NDVI vegetation index image (updated every 16 days). Returns the file path. |
| `get_satellite_SST(output_path, prefix="")` | `str` | Downloads 1 `.gif` Sea Surface Temperature image (updated daily). Returns the file path. |

---

### `aemetxfb.utils` — Utility functions

#### Configuration

| Item | Type | Description |
|------|------|-------------|
| `AEMetConfig` | `dataclass` | Immutable configuration class. Parameters: `http_timeout` (10s), `download_timeout` (60s), `cache_enabled` (False), `cache_ttl` (86400s), `cache_dir` (".aemet_cache"). |
| `CONFIG` | `AEMetConfig` | Active global configuration instance. |
| `set_config(config)` | `None` | Replaces the global configuration. |

| Function | Returns | Description |
|----------|---------|-------------|
| `decompress_file(file, output_path)` | `None` | Extracts a `.tar.gz` archive to `output_path`. Creates parent directories if needed. |
| `find_latest_timestamp(url_template, timestep, max_iterations, timestamp_format, check_method="http_status", radar_id=None)` | `datetime.datetime` | Iterates backwards in time to find the latest timestamp with available data. Uses HTTP status or JSON content check. |
| `get_spanish_day_name(day_name)` | `str` | Converts an English day name (e.g. `"Monday"`) to Spanish (e.g. `"lunes"`). |
| `get_spanish_month_name(month_name)` | `str` | Converts a month name between English and Spanish (bidirectional). Accepts any case. |

---

### `aemetxfb.cache` — Optional disk cache

Persistent `sqlite3`-backed cache for pure functions returning `pickle`-serializable data. **Disabled by default**.

| Function/Decorator | Returns | Description |
|-------------------|---------|-------------|
| `@cached` | — | Decorator that caches a function's result. Key is a SHA-256 hash of function name + arguments. Respects `CONFIG` TTL. |
| `cache_get(key)` | `Any` | Retrieves a value from the cache manually (no TTL check). |
| `cache_set(key, value)` | `None` | Stores a value in the cache manually. |
| `get_cache_stats()` | `dict` | Returns cache statistics: `{"size": bytes, "entries": int}`. |
| `clear_cache()` | `None` | Clears the entire cache database. |

Enabling:

```python
from aemetxfb import AEMetConfig, set_config

set_config(AEMetConfig(cache_enabled=True, cache_ttl=86400))
```

---

### `aemetxfb.pred` — Hourly forecasts

3-day hourly forecasts for ~8,000 Spanish municipalities. Station registry with name lookup (case-insensitive, exact or substring) and coordinate lookup (vectorised nearest-neighbour with `numpy`).

#### Station registry

| Function | Returns | Description |
|----------|---------|-------------|
| `get_predicted_stations()` | `dict[str, dict]` | Dictionary of ~8,000 stations. Each entry: `{"name": str, "lat": float, "lon": float}`. Lazy-loaded, in-memory cache. |
| `find_nearest_location(lat, lon)` | `str` | ID of the nearest station to the given coordinates (e.g. `"id28079"`). Vectorised search with `numpy`. |

#### `get_forecast(lat_or_name, lon=None)` → `Forecast`

Fetches the hourly forecast for a municipality. Accepts either a **name** (string) or **coordinates** (float, float).

```python
from aemetxfb.pred import get_forecast

# By name
forecast = get_forecast("Madrid")

# By coordinates
forecast = get_forecast(40.4168, -3.7038)
```

If the name is ambiguous (e.g. `"Mieres"` matches 2 stations), a `ValueError` is raised with details of each match. If no match is found, `ValueError` with a clear message.

#### `Forecast` object

| Method | Returns | Description |
|--------|---------|-------------|
| `get_metadata()` | `dict` | Full metadata dict: `id`, `name`, `province`, `generated_at`, `link`, etc. |
| `get_metadata(key)` | `Any` | Value for a specific key (e.g. `"name"`, `"link"`). |
| `get_sunrise_sunset()` | `pd.DataFrame` | Columns: `date`, `sunrise`, `sunset` (one row per forecast day). |
| `get_hourly()` | `pd.DataFrame` | Full hourly data. Columns: `date`, `hour`, `temperature`, `apparent_temperature`, `relative_humidity`, `precipitation`, `snow`, `wind_direction`, `wind_speed`, `gust_speed`, `sky_code`, `sky_description`. |
| `get_hourly(variable)` | `pd.DataFrame` | Subset with `date`, `hour`, and the requested variable. Aliases: `"rain"` → precipitation, `"gust"` → gust_speed, `"sky"` → sky_description, `"humidity"` → relative_humidity, `"heat_index"` → apparent_temperature. |
| `get_probability()` | `pd.DataFrame` | 6-hourly probabilities in wide format: `date`, `period`, `precipitation`, `storm`, `snow`. Periods: `0208`, `0814`, `1420`, `2002`. |
| `get_probability(variable)` | `pd.DataFrame` | Single variable: `date`, `period`, `probability`. Aliases: `"thunderstorm"` → storm. |