Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ jobs:
- run: ruff check plot_function utils tests examples
- run: pytest
- run: python examples/quickstart.py
- run: python examples/journal_quickstart.py
- run: python -m build
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,6 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

# ArcGIS schema locks
*.sr.lock
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
# Changelog

## 0.4.0 — Unreleased

- New `plot_function.journal` module: Nature/Science-style typography
(`journal_style`, Arial/Helvetica fallback chain, 7–9 pt, editable PDF/SVG text),
column-width `figsize`, discrete colormaps with extend triangles
(`discrete_cmap`), slim titled colourbars (`add_colorbar`), clean degree ticks and
dashed graticules (`geo_ticks`), cream land and borders (`add_land`),
stippling/hatching helpers with legend handles, `add_inset_bars` (stacked
low-agreement hatching), `add_inset_histogram` (stacked groups, total outline,
log bins, cumulative curve on an offset twin axis), projection-aware marginal
profiles (`add_latitude_profile`, `add_longitude_profile`), panel labels, size
legends and 600-dpi `save_figure`.
- Marginal profiles are now aligned with the map latitudes on every projection
(Robinson, Equal Earth, …), not only Plate Carrée, and follow `set_extent`.
- Restyled `plot_map`: white publication theme, thinner lines, slim colourbars,
degree tick labels on regional maps, Nature-style panel letters, 300-dpi export.
- Restyled distribution insets (white translucent backdrop, open spines, mean
marker) and profile panels (labels below the axis, dashed latitude guides).
- New gallery (`examples/journal_figures.py`, `docs/gallery/`) and redesigned
minimalist line-drawing logo and banner (`docs/brand/*.svg`).
- Bilingual README, `CITATION.cff`, richer package metadata.

## 0.3.0 — Unreleased

- Add right/top profiles with line, spread-band, and bar styles.
Expand Down
20 changes: 20 additions & 0 deletions CITATION.cff
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
cff-version: 1.2.0
message: "If plot-function helped your figures, please cite it as below and give the repository a star."
title: "plot-function: journal-grade maps from NetCDF with xarray, Cartopy and Matplotlib"
type: software
authors:
- family-names: Wang
given-names: Longhao
orcid: "https://orcid.org/0000-0002-4642-4701"
repository-code: "https://github.com/GISWLH/plot-function"
url: "https://github.com/GISWLH/plot-function"
license: MIT
version: 0.4.0
keywords:
- netcdf
- cartopy
- xarray
- matplotlib
- climate
- hydrology
- scientific visualization
3 changes: 2 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
include README*.md CHANGELOG.md CONTRIBUTING.md LICENSE
recursive-include docs *.md *.png
recursive-include docs *.md *.png *.svg
recursive-include examples *.py
recursive-include tests *.py
include CITATION.cff
4 changes: 2 additions & 2 deletions README.ja.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<p align="center"><img src="docs/assets/logo.png" width="72" alt="plot-function のロゴ"></p>
<p align="center"><img src="docs/brand/banner.png" width="100%" alt="plot-function — NetCDF から論文品質の地図へ"></p>

![plot-function — NetCDF から美しい地図へ](docs/assets/header.png)
> **0.4 の新機能:** Nature / Science 風の図版ツールキット `plot_function.journal`(離散カラースケール、点描・ハッチング、左下の統計インセット、地図の緯度に揃えた右側プロファイル、600 dpi 出力)。[新ギャラリー](README.md#gallery) と [API](docs/API.md#plot_functionjournal) を参照してください。

<p align="center"><a href="README.md">English</a> · <a href="README.zh-CN.md">简体中文</a> · <b>日本語</b></p>
<p align="center">
Expand Down
334 changes: 127 additions & 207 deletions README.md

Large diffs are not rendered by default.

17 changes: 13 additions & 4 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
<p align="center"><img src="docs/assets/logo.png" width="72" alt="plot-function 标志"></p>

![plot-function — 从 NetCDF 到精美地图](docs/assets/header.png)
<p align="center"><img src="docs/brand/banner.png" width="100%" alt="plot-function — 几行代码,从 NetCDF 到顶刊级地学制图"></p>

<p align="center"><a href="README.md">English</a> · <b>简体中文</b> · <a href="README.ja.md">日本語</a></p>
<p align="center">
Expand All @@ -13,7 +11,18 @@

新工作流无需准备 Shapefile 或 GeoTIFF,也不依赖 Salem。可选海岸线由 Cartopy 的 Natural Earth 缓存提供;设置 `coastlines=False` 即可完全离线绘图。

[快速开始](#快速开始) · [示例画廊](#示例画廊) · [使用自己的数据](#使用自己的数据) · [命令行](#命令行) · [API 参考](docs/API.md)
[顶刊图库](#顶刊图库04-新增) · [快速开始](#快速开始) · [示例画廊](#示例画廊) · [使用自己的数据](#使用自己的数据) · [命令行](#命令行) · [API 参考](docs/API.md)

## 顶刊图库(0.4 新增)

新增 `plot_function.journal` 模块,参照 Nature / Science 版式:Arial 7–9 pt 字体、带三角端点的离散色标、低一致性打点与纹理、左下角统计小图(分类柱状图带低一致性纹理 / 对数直方图 + 累积曲线双轴)、右侧**与地图纬度严格对齐**的纬向剖面(Robinson 等任意投影均对齐,含 IQR / 集合范围阴影)、`40°N` 式刻度与浅色虚线经纬网、600 dpi 导出。

| | |
| :---: | :---: |
| <img src="docs/gallery/fig1_regimes.png" alt="双色标分区图、打点、左下角柱状图与右侧纬向剖面"> | <img src="docs/gallery/fig2_sites.png" alt="站点图、对数直方图与累积曲线"> |
| <img src="docs/gallery/fig3_era5_profile.png" alt="Robinson 投影与对齐的纬向剖面"> | <img src="docs/gallery/fig4_regional_significance.png" alt="区域打点与纹理显著性对比"> |

图 1、2、4 使用**合成示例数据**(图中已注明),图 3 使用仓库自带的 ERA5 1978 年数据。运行 `python examples/journal_figures.py` 可全部复现;最小示例见 `python examples/journal_quickstart.py`,用法见英文 [README](README.md#journal-toolkit) 与 [API 参考](docs/API.md#plot_functionjournal)。

## 快速开始

Expand Down
Empty file.
48 changes: 45 additions & 3 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ Accepts all `open_field` options, plus:
| `colorbar` | `True` | Add a horizontal colorbar. Disable for shared custom colorbars. |
| `plotfunc` | `"pcolormesh"` | `pcolormesh` or `contourf`. |
| `ax` | `None` | An existing Cartopy GeoAxes; overrides `projection`. Its figure background remains caller-controlled. |
| `figsize` | `(10, 5.8)` | Inches, used only when creating a new figure. |
| `figsize` | `(7.2, 4.2)` | Inches (≈183 mm double column), used only when creating a new figure. |
| `output` | `None` | Optional path to save immediately. Parent directories are created. |
| `dpi` | `180` | Resolution for immediate export. |
| `dpi` | `300` | Resolution for immediate export; use 600 for print. |

Regional extents control the viewport; they do not crop, mask, or resample the
returned data. For very large arrays, select/crop the data before plotting.
Expand All @@ -89,7 +89,7 @@ returned data. For very large arrays, select/crop the data before plotting.
| `.artist` | The plotted mappable, usable for a shared colorbar. |
| `.colorbar` | Colorbar or `None`. |
| `.data` | The normalized 2D field, including explicitly converted values. |
| `.save(path, dpi=180, **kwargs)` | Save with a tight bounding box and the figure's background. Additional options go to `Figure.savefig`. Returns a `Path`. |
| `.save(path, dpi=300, **kwargs)` | Save with a tight bounding box and the figure's background. Additional options go to `Figure.savefig`. Returns a `Path`. |

```python
result.axes.text(0.02, 0.02, "ERA5 · 1978", transform=result.axes.transAxes)
Expand Down Expand Up @@ -137,3 +137,45 @@ preserving the original API's behavior.
`add_profile`, `add_distribution`, `add_significance`, and `add_features` accept
an option object or its keyword arguments. See [Research figures](RESEARCH_FIGURES.md)
for styles, weights, scope, algorithms, defaults, and examples.

## Data contract

- **Rectilinear geographic grids:** latitude and longitude must each be one-dimensional, finite, and contain at least two distinct values. CF `standard_name` / geographic `units` and common `lat`/`lon` or `latitude`/`longitude` names are recognized.
- **Explicit scientific choices:** multiple fields require `variable=`; nonspatial dimensions with more than one value require selection or reduction. Reductions are unweighted and skip missing values. Supported statistics: `mean`, `median`, `min`, `max`, `sum`, `std`.
- **Coordinate normalization:** latitude is sorted, longitude is wrapped to `[-180, 180)` and sorted; a redundant global 0°/360° endpoint is removed. Other duplicates are rejected.
- **Missing values:** xarray decodes NetCDF fill values; nonfinite values are masked. An entirely missing field produces an actionable error.
- **Bounded scope:** curvilinear grids, unstructured meshes, projected x/y grids in metres, and antimeridian-crossing regional grids/extents need preprocessing. The API does not reproject source grids or infer units, vertical levels, time weights, or area weights.
- **Memory:** the selected/reduced 2D field is loaded before the file is closed. For large workflows, preprocess with xarray and pass a prepared `DataArray`.

## `plot_function.journal`

Publication helpers that decorate *any* Matplotlib/Cartopy axes. They never
modify global `rcParams`; wrap figure code in `journal_style()`. Gallery:
[`examples/journal_figures.py`](../examples/journal_figures.py); minimal example:
[`examples/journal_quickstart.py`](../examples/journal_quickstart.py).

| Function | Key arguments | Returns |
| :--- | :--- | :--- |
| `journal_style(**overrides)` | any rcParams | context manager (Arial → Helvetica → Liberation Sans → Nimbus Sans → DejaVu Sans, 7 pt base, 0.5 pt lines, `pdf.fonttype=42`) |
| `figsize(width="double", ratio=0.55)` | `"single"` (89 mm), `"onehalf"` (120 mm), `"double"` (183 mm) or mm | `(w, h)` inches |
| `discrete_cmap(cmap, levels, extend="both")` | colormap name/object or explicit colour list (`len(levels)-1` + number of extend ends) | `(ListedColormap, BoundaryNorm)` |
| `add_colorbar(mappable, ax, label=, title=, orientation=, bounds=, tick_every=)` | `bounds=[x, y, w, h]` in figure fraction, or placement relative to `ax` | `Colorbar` |
| `geo_ticks(ax, xticks=, yticks=, lat_labels=, lon_labels=, gridlines=)` | degree tick positions | gridliner list |
| `add_land(ax, color=LAND, borders=True, resolution="110m")` | Natural Earth (downloads once) | feature artists |
| `add_stippling(ax, lon, lat, mask, stride=2, size=1.2, offset=True)` | 1-D or 2-D lon/lat, boolean mask | `PathCollection` or `None` |
| `add_hatching(ax, lon, lat, mask, hatch="....")` | | contour set or `None` |
| `hatch_patch(hatch)` | | legend handle |
| `add_inset_bars(ax, labels, values, hatched=, colors=, bounds=, frame="box")` | `hatched` = uncertain part of each bar | inset axes |
| `add_inset_histogram(ax, groups, bins=, log=, cumulative=, bounds=, frame="open")` | array or `{label: values}`; `cumulative=True` or weights | `(hist_axes, twin_axes or None)` |
| `add_latitude_profile(ax, lat, center, lower=, upper=, outer=, members=, width=, pad=)` | values per latitude | profile axes, y axis locked to the map |
| `add_longitude_profile(ax, lon, center, lower=, upper=)` | values per longitude | profile axes above the map |
| `add_panel_label(ax, "a", style="({})")` | | `Text` |
| `add_size_legend(ax, values, labels, scale=)` | `scale(value) -> marker area (pt²)` | `Legend` |
| `save_figure(fig, path, dpi=600, formats=None)` | e.g. `formats=("png", "pdf")` | path(s) |

**Profile alignment.** Profile coordinates are transformed with
`ax.projection.transform_points` at the central longitude (or the centre of
the extent), and the profile's limits are synchronised with the map's at draw
time. On pseudo-cylindrical projections (Robinson, Equal Earth, Mollweide)
latitude maps to a single y value, so alignment is exact; on conic projections
it is exact only along the reference meridian.
23 changes: 18 additions & 5 deletions docs/assets/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,25 @@ plots, not image-generated depictions of data.

The code's MIT license does not replace the applicable terms of source datasets.

## Decorative branding
## Branding

`header.png` and `logo.png` are AI-generated decorative artwork created for this
project. They are separate assets, both designed on white backgrounds; the header
does not embed the standalone logo. The abstract contour shapes are not observations, model outputs, or
geographic boundaries. They are deliberately separate from the computed gallery.
The logo and banner live in [`docs/brand/`](../brand/). They are hand-authored
SVG line drawings (white background) of the library's signature layout — a map
with isolines and stippling, a lower-left histogram inset, a right-hand latitude
profile and a discrete colourbar. They are decorative and contain no data.
PNG exports (`logo.png`, `banner.png`) were rendered from the SVGs with CairoSVG;
the banner's text uses Inter and Noto Sans CJK SC when rendered.

## Journal gallery (0.4)

`docs/gallery/*.png` are generated by `examples/journal_figures.py`:

- `fig1_regimes.png`, `fig2_sites.png`, `fig4_regional_significance.png`:
**synthetic example data** (seeded random fields, pseudo-ensemble members,
random sites and z-test p-values). They demonstrate layout only and carry no
scientific meaning; each figure says so in its footnote.
- `fig3_era5_profile.png`: the bundled ERA5 1978 file, July minus the
12-month mean, with inter-month IQR/range bands on the zonal profile.

## Reproduction

Expand Down
Binary file modified docs/assets/east-asia.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/assets/global-temperature.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/assets/header.png
Binary file not shown.
Binary file modified docs/assets/journal-global.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/assets/journal-regional.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/assets/journal-significance.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed docs/assets/logo.png
Binary file not shown.
Binary file modified docs/assets/seasonal-contrast.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/assets/seasons.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/brand/banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
56 changes: 56 additions & 0 deletions docs/brand/banner.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/brand/logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
43 changes: 43 additions & 0 deletions docs/brand/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/gallery/fig1_regimes.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/gallery/fig2_sites.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/gallery/fig3_era5_profile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/gallery/fig4_regional_significance.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading