Metadata-Version: 2.3
Name: volcano-pygmt
Version: 0.2.0
Summary: Plot volcano on map
Keywords: volcano,plot,volcanology,volcanoes
Author: Martanto
Author-email: Martanto <martanto@live.com>
License: MIT License
         
         Copyright (c) 2026 Martanto
         
         Permission is hereby granted, free of charge, to any person obtaining a copy
         of this software and associated documentation files (the "Software"), to deal
         in the Software without restriction, including without limitation the rights
         to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         copies of the Software, and to permit persons to whom the Software is
         furnished to do so, subject to the following conditions:
         
         The above copyright notice and this permission notice shall be included in all
         copies or substantial portions of the Software.
         
         THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         SOFTWARE.
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Dist: dotenv>=0.9.9
Requires-Dist: loguru>=0.7.3
Requires-Dist: pygmt>=0.18.0
Requires-Dist: rioxarray>=0.22.0
Requires-Python: >=3.12
Project-URL: Homepage, https://github.com/martanto/volcano-pygmt
Project-URL: Issues, https://github.com/martanto/volcano-pygmt/issues
Description-Content-Type: text/markdown

# volcano-pygmt

![Version](https://img.shields.io/badge/version-0.2.0-blue)
[![PyPI](https://img.shields.io/pypi/v/volcano-pygmt)](https://pypi.org/project/volcano-pygmt/)
![Python](https://img.shields.io/badge/python-3.12%2B-blue)
![License](https://img.shields.io/badge/license-MIT-green)
![Status](https://img.shields.io/badge/status-active%20development-orange)

A Python package for plotting volcano maps using [PyGMT](https://www.pygmt.org). Generate scientific-quality maps with terrain relief, contours, seismic station markers, scale bars, north arrows, and country-level locator insets.

<table>
<tr>
  <td align="center">
    <a href="https://raw.githubusercontent.com/martanto/volcano-pygmt/main/assets/semeru.png"><img src="https://raw.githubusercontent.com/martanto/volcano-pygmt/main/assets/semeru.png" width="240" alt="Semeru volcano map"></a>
    <br><b>Semeru</b> · East Java, Indonesia
    <br><sub>Contour interval 300 m · padding 20 km</sub>
  </td>
  <td align="center">
    <a href="https://raw.githubusercontent.com/martanto/volcano-pygmt/main/assets/lewotobi-laki-laki.png"><img src="https://raw.githubusercontent.com/martanto/volcano-pygmt/main/assets/lewotobi-laki-laki.png" width="240" alt="Lewotobi Laki-laki volcano map"></a>
    <br><b>Lewotobi Laki-laki</b> · East Nusa Tenggara, Indonesia
    <br><sub>Contour interval 300 m · padding 10 km</sub>
  </td>
</tr>
</table>

## Table of Contents

- [Requirements](#requirements)
- [Configuration](#configuration)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Examples](#examples)
- [API Reference](#api-reference)
- [License](#license)

## Requirements

- **Python** `>=3.12`
- **uv** — Python package manager ([installation guide](https://docs.astral.sh/uv/getting-started/installation/))
- **GMT** (Generic Mapping Tools) — required by PyGMT ([download](https://www.generic-mapping-tools.org/download/), [troubleshooting](https://www.pygmt.org/latest/install.html#error-loading-gmt-shared-library-at))

## Configuration

> [!IMPORTANT]
> `GMT_LIBRARY_PATH` must be set in your environment **before** `pygmt` is imported. Importing PyGMT without it will raise a library-not-found error.

If PyGMT cannot find the GMT shared library at import time, set `GMT_LIBRARY_PATH` to the directory that contains the GMT shared library (`gmt_c.dll` on Windows, `libgmt.dylib` on macOS, `libgmt.so` on Linux).

### 1. Copy the example file

```bash
cp .env.example .env
```

Open `.env` in any text editor. It will look like this:

```env
GMT_LIBRARY_PATH=
```

Fill in the path to the directory containing the GMT shared library:

```env
GMT_LIBRARY_PATH=C:/Program Files/GMT6/bin
```

### 2. Edit `.env` and set the path

```env
GMT_LIBRARY_PATH=/path/to/gmt/lib
```

**Platform examples:**

| Platform | Typical value |
|----------|---------------|
| Windows | `C:/Program Files/GMT6/bin` |
| macOS (Homebrew) | `/opt/homebrew/lib` |
| Linux | `/usr/lib/x86_64-linux-gnu` |

> **Tip:** Run `gmt --version` in your terminal to confirm GMT is installed, then locate the library with `find / -name "libgmt*" 2>/dev/null` (Linux/macOS) or `where gmt` (Windows). The library lives in the same directory as (or adjacent to) the `gmt` executable.

### 3. How it works

The package calls `load_config()` automatically on import, which reads `.env` and injects `GMT_LIBRARY_PATH` into the process environment before PyGMT is imported. You do not need to set the variable globally in your shell — the `.env` file is enough.

If you need to load the configuration manually (e.g. in a standalone script that imports PyGMT directly), call `load_config()` before any PyGMT import:

```python
from volcano_pygmt.config import load_config

load_config()  # reads .env and sets GMT_LIBRARY_PATH

import pygmt  # now safe to import
```

## Installation

### From PyPI

```bash
pip install volcano-pygmt
```

### From source

1. Clone the repository:

```bash
git clone https://github.com/martanto/volcano-pygmt.git
cd volcano-pygmt
```

2. Install dependencies with `uv`:

```bash
uv sync
```

This installs all runtime dependencies (`pygmt`, `rioxarray`, `loguru`, `dotenv`) and dev tools (`ruff`, `ty`, `pytest`, etc.).

## Quick Start

```python
from volcano_pygmt import plot

maps = [
    {
        "padding_km": 20,
        "volcano": {"lon": 112.922, "lat": -8.108, "elev": 3672, "name": "Semeru"},
        "stations": {
            "VG.LEKR.EHZ.00": {"lat": -8.137244444, "lon": 112.9858444},
        },
        "contour": True,
        "contour_interval": 300.0,
        "contour_annotation": 600.0,
        "colorbar": True,
    },
]

files = plot(maps)
print(files)  # [PosixPath('.../output/semeru.png')]
```

Output PNG files are saved to `./output/<volcano-name-slug>.png`.

See `main.py` for a more complete example with multiple volcanoes and local DEM files.

## Examples

### Multiple volcanoes in a batch

The following example (from `main.py`) plots three Indonesian volcanoes — Semeru, Lewotobi Laki-laki, and Ruang — each with one seismic station and contour lines. Semeru and Lewotobi use local DEM files; Ruang falls back to SRTM data:

```python
from volcano_pygmt import plot

maps = [
    {
        "volcano": {"lon": 112.922, "lat": -8.108, "elev": 3672, "name": "Semeru"},
        "stations": {
            "VG.LEKR.EHZ.00": {"lat": -8.137244444, "lon": 112.9858444},
        },
        "dem_files": [
            "DEMNAS_1608-12_v1.0.tif",
            "DEMNAS_1608-21_v1.0.tif",
            "DEMNAS_1607-42_v1.0.tif",
            "DEMNAS_1607-44_v1.0.tif",
            "DEMNAS_1607-53_v1.0.tif",
        ],
        "padding_km": 20,
        "color_relief": False,
        "contour": True,
        "contour_interval": 300.0,
        "contour_annotation": 600.0,
        "colorbar": True,
    },
    {
        "volcano": {
            "lon": 122.775,
            "lat": -8.542,
            "elev": 3672,
            "name": "Lewotobi Laki-laki",
        },
        "stations": {
            "VG.OJN.EHZ.00": {"lat": -8.502944444, "lon": 122.7737222},
        },
        "dem_files": [
            "DEMNAS_2207-33_v1.0.tif",
            "DEMNAS_2207-34_v1.0.tif",
            "DEMNAS_2207-61_v1.0.tif",
            "DEMNAS_2207-62_v1.0.tif",
        ],
        "padding_km": 10,
        "color_relief": False,
        "contour": True,
        "contour_interval": 100.0,
        "contour_annotation": 300.0,
        "colorbar": True,
    },
    {
        "volcano": {"lon": 125.3667, "lat": 2.3031, "elev": 703, "name": "Ruang"},
        "stations": {
            "VG.RUA3.EHZ.00": {"lat": 2.3196, "lon": 125.3814},
        },
        "padding_km": 5,
        "color_relief": False,
        "contour": True,
        "contour_interval": 200.0,
        "contour_annotation": 200.0,
        "colorbar": True,
    },
]

plot(maps, "png")
```

Run it with:

```bash
uv run main.py
```

### Single volcano with color relief and hillshade

```python
from volcano_pygmt.plot import create_figure

volcano = {"lon": 107.65, "lat": -6.9, "name": "Tangkuban Parahu"}

fig = create_figure(
    volcano,
    padding_km=10.0,
    hillshade=True,
    contour=True,
    contour_interval=100.0,
    color_relief=True,
    colorbar=True,
    relief_cmap="gmt/haxby",
)

fig.savefig("tangkuban-parahu.png")
```

## API Reference

### `plot(maps, file_type, water_color)`

Batch-render a list of volcano map specs and save each as a file to `./output/<slug>.<file_type>`.

**Parameters:**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `maps` | `list[dict]` | required | List of map specification dicts (see keys below) |
| `file_type` | `str` | `"png"` | Output file format: `"png"` or `"pdf"` |
| `water_color` | `str` | `"lightblue"` | Default water fill colour for all maps using DEM files (overridden per-map by `"water_color"` key) |

**Returns:** `list[pathlib.Path]` — absolute paths to the saved files, in input order.

**Map spec keys:**

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `volcano` | `dict` | required | Volcano info — must include `"lon"` (float), `"lat"` (float), `"name"` (str) |
| `padding_km` | `float` | required | Half-extent of the map around the volcano centre in kilometres |
| `stations` | `dict \| None` | `None` | Station codes mapped to `{"lon": float, "lat": float}`; omit or set `None` to skip |
| `dem_files` | `list[str] \| None` | `None` | Local GeoTIFF DEM file paths; falls back to SRTM download when `None` |
| `country` | `str` | `"Indonesia"` | Country name for the locator inset (must be in `COUNTRY_REGIONS`) |
| `hillshade` | `bool` | `False` | Render a grayscale shaded-relief layer |
| `contour` | `bool` | `False` | Draw elevation contour lines |
| `contour_interval` | `float` | `100.0` | Spacing between contour lines in metres |
| `contour_annotation` | `float \| None` | `None` | Contour label interval in metres; defaults to `5 × contour_interval` when `None` |
| `color_relief` | `bool` | `False` | Render a colour-filled elevation image |
| `colorbar` | `bool` | `False` | Add an elevation colorbar (requires `color_relief=True`) |
| `relief_cmap` | `str` | `"gmt/haxby"` | GMT colormap for colour relief — any valid GMT CPT name (e.g. `"geo"`, `"topo"`, `"dem1"`, `"gray"`) |
| `show_title` | `bool` | `False` | Display the volcano name as the map title |
| `water_color` | `str` | `"lightblue"` | Fill colour for water areas when `dem_files` is set — `"white"`, `"blue"`, `"lightblue"`, or `"lightgray"` |

---

### `create_figure(volcano, stations, padding_km, ...)`

Create and return a fully composed `pygmt.Figure` for a single volcano. Includes coastlines, optional terrain relief, volcano and station symbols, a legend, a scale bar, a north-arrow rose, and a country-level locator inset.

**Parameters:**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `volcano` | `dict` | required | Must include `"lon"` (float), `"lat"` (float), `"name"` (str) |
| `stations` | `dict \| None` | `None` | Station codes mapped to `{"lon": float, "lat": float}`; omit or set `None` to skip |
| `padding_km` | `float` | `5.0` | Half-extent of the map around the volcano centre in kilometres |
| `country` | `str` | `"Indonesia"` | Country name for the locator inset |
| `dem_files` | `list[str] \| None` | `None` | Local GeoTIFF DEM file paths; falls back to SRTM download when `None` |
| `hillshade` | `bool` | `False` | Render a grayscale shaded-relief layer |
| `contour` | `bool` | `False` | Draw elevation contour lines |
| `contour_interval` | `float` | `100.0` | Spacing between contour lines in metres |
| `contour_annotation` | `float \| None` | `None` | Contour label interval in metres; defaults to `5 × contour_interval` when `None` |
| `color_relief` | `bool` | `False` | Render a colour-filled elevation image |
| `colorbar` | `bool` | `False` | Add an elevation colorbar (requires `color_relief=True`) |
| `relief_cmap` | `str` | `"gmt/haxby"` | GMT colormap for colour relief |
| `show_title` | `bool` | `True` | Display the volcano name as the map title |
| `water_color` | `str` | `"lightblue"` | Fill colour for water areas when `dem_files` is set — `"white"`, `"blue"`, `"lightblue"`, or `"lightgray"` |

**Returns:** `pygmt.Figure` — fully rendered figure ready to save or display.

---

### `add_relief(fig, region, projection, ...)`

Download SRTM 3-arc-second earth relief for `region` and composite one or more terrain layers onto `fig`.

**Parameters:**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `fig` | `pygmt.Figure` | required | The PyGMT figure to draw on |
| `region` | `list[float]` | required | Map extent as `[lon_min, lon_max, lat_min, lat_max]` in decimal degrees |
| `projection` | `str` | required | PyGMT/GMT projection string, e.g. `"M10c"` |
| `hillshade` | `bool` | `False` | Render a grayscale shaded-relief layer (radiance 315°/45°, exponential normalisation) |
| `contour` | `bool` | `False` | Draw elevation contour lines |
| `contour_interval` | `float` | `100.0` | Spacing between contour lines in metres |
| `contour_annotation` | `float \| None` | `None` | Contour label interval in metres; defaults to `5 × contour_interval` when `None` |
| `color_relief` | `bool` | `False` | Render a colour-filled elevation image using `relief_cmap` |
| `colorbar` | `bool` | `False` | Add an elevation colorbar labelled in metres (requires `color_relief=True`) |
| `relief_cmap` | `str` | `"gmt/haxby"` | GMT colormap name for the colour-relief image |

**Returns:** `pygmt.Figure` — the same `fig` with the requested layers added in-place.

---

### `plot_from_dem(fig, dem_files, projection, ...)`

Load one or more local GeoTIFF DEM files and composite hillshade, color relief, and/or contours onto `fig`.

**Parameters:**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `fig` | `pygmt.Figure` | required | The PyGMT figure to draw on |
| `dem_files` | `str \| list[str]` | required | Path or list of paths to GeoTIFF DEM files; multiple files are merged before plotting |
| `projection` | `str` | required | PyGMT/GMT projection string, e.g. `"M10c"` |
| `region` | `list[float] \| None` | `None` | Map extent as `[lon_min, lon_max, lat_min, lat_max]`; uses full DEM extent when `None` |
| `hillshade` | `bool` | `False` | Render a grayscale shaded-relief layer |
| `contour` | `bool` | `False` | Draw elevation contour lines |
| `contour_interval` | `float` | `100.0` | Spacing between contour lines in metres |
| `contour_annotation` | `float \| None` | `None` | Contour label interval in metres; defaults to `5 × contour_interval` when `None` |
| `color_relief` | `bool` | `False` | Render a colour-filled elevation image |
| `colorbar` | `bool` | `False` | Add a colorbar showing the elevation scale |
| `relief_cmap` | `str` | `"gmt/haxby"` | GMT colormap name for the colour-relief image |
| `water_color` | `str` | `"lightblue"` | Fill colour for sea/water areas — `"white"`, `"blue"`, `"lightblue"`, or `"lightgray"` |

**Returns:** `pygmt.Figure` — the same `fig` with the requested layers added in-place.

---

### `add_inset(fig, volcano, country)`

Add a country-level locator inset to the bottom-left corner of `fig`, with a red star marking the volcano position.

**Parameters:**

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `fig` | `pygmt.Figure` | required | The PyGMT figure to add the inset to |
| `volcano` | `dict` | required | Must include `"lon"` (float) and `"lat"` (float) |
| `country` | `str` | `"Indonesia"` | Country name to look up in `COUNTRY_REGIONS` for the inset extent |

**Returns:** `pygmt.Figure` — the same `fig` with the inset added in-place.

**Raises:** `ValueError` — if `country` is not a key in `COUNTRY_REGIONS`.

## Development

```bash
# Run tests
uv run pytest

# Lint
uv run ruff check src/

# Auto-fix lint issues
uv run ruff check --fix src/

# Type check
uv run ty check

# Start Jupyter notebook
uv run jupyter notebook
```

## License

MIT — see [LICENSE](LICENSE) for details.
