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

JoranAngevaare / optim_esm_tools / 14750979690

30 Apr 2025 09:10AM UTC coverage: 89.832% (+0.02%) from 89.81%
14750979690

Pull #238

github

web-flow
Merge 214c497cc into c00e6db3c
Pull Request #238: Further sort candidate regions on lat lon in `Merger`

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

3 existing lines in 1 file 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
    *a,
13
    projection: ty.Optional[str] = None,
14
    coastlines: bool = True,
15
    add_features: bool = False,
16
    add_gridlines: bool = True,
17
    coast_line_kw: ty.Optional[dict] = None,
18
    gridline_kw: ty.Optional[dict] = None,
19
    no_top_labels: bool = True,
20
    **projection_kwargs,
21
):
22
    plt.gcf().add_subplot(
4✔
23
        *a,
24
        projection=get_cartopy_projection(projection, **projection_kwargs),
25
    )
26
    ax = plt.gca()
4✔
27
    if coastlines:
4✔
28
        coast_line_kw = coast_line_kw or dict()
4✔
29
        ax.coastlines(**coast_line_kw)
4✔
30
    if add_features:
4✔
31
        import cartopy.feature as cfeature
4✔
32

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

43

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

51

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

61

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

70

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

79

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

83

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

87

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

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

100

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

111

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

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

143

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

155

156
def get_ylabel(ds: xr.Dataset, var: ty.Optional[str] = None):
4✔
157
    var = var or ds.attrs.get('variable_id', 'var')
4✔
158
    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