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

JoranAngevaare / optim_esm_tools / 14750976719

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

Pull #238

github

web-flow
Merge 0b71c9943 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%)

4 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(*a,
4✔
23
        projection=get_cartopy_projection(projection, **projection_kwargs),
24
    )
25
    ax = plt.gca()
4✔
26
    if coastlines:
4✔
27
        coast_line_kw = coast_line_kw or dict()
4✔
28
        ax.coastlines(**coast_line_kw)
4✔
29
    if add_features:
4✔
30
        import cartopy.feature as cfeature
4✔
31

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

42

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

50

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

60

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

69

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

78

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

82

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

86

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

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

99

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

110

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

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

142

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

154

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