• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

JoranAngevaare / optim_esm_tools / 14750707513

30 Apr 2025 08:55AM UTC coverage: 89.832% (+0.02%) from 89.81%
14750707513

Pull #238

github

web-flow
Merge 594f364cc into 90367cc2c
Pull Request #238: Further sort candidate regions on lat lon in `Merger`

9 of 9 new or added lines in 2 files covered. (100.0%)

224 existing lines in 7 files now uncovered.

2942 of 3275 relevant lines covered (89.83%)

3.31 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.85
/optim_esm_tools/plotting/plot.py
1
import typing as ty
4✔
2

3
import matplotlib.pyplot as plt
4✔
4
import xarray as xr
4✔
5

6
import optim_esm_tools as oet
4✔
7
from optim_esm_tools.config import config
4✔
8
from optim_esm_tools.config import get_logger
4✔
9

10

11
def setup_map(
4✔
12
    projection: ty.Optional[str] = None,
13
    coastlines: bool = True,
14
    add_features: bool = False,
15
    add_gridlines: bool = True,
16
    coast_line_kw: ty.Optional[dict] = None,
17
    gridline_kw: ty.Optional[dict] = None,
18
    no_top_labels: bool = True,
19
    **projection_kwargs,
20
):
21
    plt.gcf().add_subplot(
4✔
22
        projection=get_cartopy_projection(projection, **projection_kwargs),
23
    )
24
    ax = plt.gca()
4✔
25
    if coastlines:
4✔
26
        coast_line_kw = coast_line_kw or dict()
4✔
27
        ax.coastlines(**coast_line_kw)
4✔
28
    if add_features:
4✔
29
        import cartopy.feature as cfeature
4✔
30

31
        allowed = 'LAND OCEAN COASTLINE BORDERS LAKES RIVERS'.split()
4✔
32
        for feat in oet.utils.to_str_tuple(add_features):
4✔
33
            assert feat.upper() in allowed, f'{feat} not in {allowed}'
4✔
34
            ax.add_feature(getattr(cfeature, feat.upper()))
4✔
35
    if add_gridlines:
4✔
36
        gridline_kw = gridline_kw or dict(draw_labels=True)
4✔
37
        gl = ax.gridlines(**gridline_kw)
4✔
38
        if no_top_labels:
4✔
39
            gl.top_labels = False
4✔
40

41

42
def _show(show):
4✔
43
    if show:
4✔
44
        plt.show()  # pragma: no cover
45
    else:
46
        plt.clf()
4✔
47
        plt.close()
4✔
48

49

50
def default_variable_labels() -> ty.Dict[str, str]:
4✔
51
    labels = dict(config['variable_label'].items())
4✔
52
    ma = config['analyze']['moving_average_years']
4✔
53
    for k, v in list(labels.items()):
4✔
54
        labels[f'{k}_detrend'] = f'Detrend {v}'
4✔
55
        labels[f'{k}_run_mean_{ma}'] = f'$RM_{{{ma}}}$ {v}'
4✔
56
        labels[f'{k}_detrend_run_mean_{ma}'] = f'Detrend $RM_{{{ma}}}$ {v}'
4✔
57
    return labels
4✔
58

59

60
def get_range(var: str) -> ty.List[float]:
4✔
61
    r = (
4✔
62
        dict(oet.config.config['variable_range'].items())
63
        .get(var, 'None,None')
64
        .split(',')
65
    )
66
    return [(float(l) if l != 'None' else None) for l in r]
4✔
67

68

69
def set_y_lim_var(var: str) -> None:
4✔
70
    d, u = get_range(var)
4✔
71
    cd, cu = plt.ylim()
4✔
72
    plt.ylim(
4✔
73
        cd if d is None else min(cd, d),
74
        cu if u is None else max(cu, u),
75
    )
76

77

78
def get_unit_da(da: xr.DataArray) -> str:
4✔
79
    return da.attrs.get('units', '?').replace('%', r'\%')
4✔
80

81

82
def get_unit(ds: xr.Dataset, var: str) -> str:
4✔
83
    return get_unit_da(ds[var])
4✔
84

85

86
def get_cartopy_projection(
4✔
87
    projection: ty.Optional[ty.Any] = None,
88
    _field: str = 'projection',
89
    **projection_kwargs,
90
) -> ty.Any:
91
    import cartopy.crs as ccrs
4✔
92

93
    projection = projection or config['cartopy'][_field]
4✔
94
    if not hasattr(ccrs, projection):
4✔
95
        raise ValueError(f'Invalid projection {projection}')  # pragma: no cover
96
    return getattr(ccrs, projection)(**projection_kwargs)
4✔
97

98

99
def get_cartopy_transform(
4✔
100
    projection: ty.Optional[ty.Any] = None,
101
    **projection_kwargs,
102
) -> ty.Any:
103
    return get_cartopy_projection(
4✔
104
        projection=projection,
105
        _field='transform',
106
        **projection_kwargs,
107
    )
108

109

110
def get_xy_lim_for_projection(
4✔
111
    projection: ty.Optional[str] = None,
112
) -> ty.Tuple[ty.Tuple[float, float], ty.Tuple[float, float]]:
113
    """Blunt hardcoding for the different projections.
114

115
    Calling plt.xlim(0, 360) will have vastly different outcomes
116
    depending on the projection used. Here we hardcoded some of the more
117
    common.
118
    """
UNCOV
119
    projection = projection or config['cartopy']['projection']
×
120
    lims = dict(
×
121
        Robinson=(
122
            (-17005833.33052523, 17005833.33052523),
123
            (-8625154.6651, 8625154.6651),
124
        ),
125
        EqualEarth=(
126
            (-17243959.06221695, 17243959.06221695),
127
            (-8392927.59846645, 8392927.598466456),
128
        ),
129
        Mollweide=(
130
            (-18040095.696147293, 18040095.696147293),
131
            (-9020047.848073646, 9020047.848073646),
132
        ),
133
        PlateCarree=((0, 360), (-90, 90)),
134
    )
UNCOV
135
    if projection not in lims:
×
136
        get_logger().warning(
137
            f'No hardcoded x/y lims for {projection}, might yield odd figures.',
138
        )  # pragma: no cover
UNCOV
139
    return lims.get(projection, ((0, 360), (-90, 90)))
×
140

141

142
def plot_da(
4✔
143
    da: xr.DataArray,
144
    projection: ty.Optional[str] = None,
145
    setup_kw: ty.Optional[dict] = None,
146
    **kw,
147
):
148
    """Simple wrapper for da.plot() with correct transforms and projections."""
149
    setup_kw = setup_kw or dict()
4✔
150
    setup_map(projection=projection, **setup_kw)
4✔
151
    da.plot(transform=get_cartopy_transform(), **kw)
4✔
152

153

154
def get_ylabel(ds: xr.Dataset, var: ty.Optional[str] = None):
4✔
155
    var = var or ds.attrs.get('variable_id', 'var')
4✔
156
    return f'{oet.plotting.plot.default_variable_labels().get(var, var)} [{get_unit(ds, var)}]'
4✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc