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

pytroll / pyresample / 4653744392

pending completion
4653744392

Pull #511

github

GitHub
Merge edb481dd6 into 7ca8789a3
Pull Request #511: Bump pypa/gh-action-pypi-publish from 1.8.4 to 1.8.5

12270 of 13075 relevant lines covered (93.84%)

3.75 hits per line

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

42.37
/pyresample/plot.py
1
#!/usr/bin/env python
2
# encoding: utf8
3
#
4
# Copyright (C) 2010-2019 Pytroll
5
#
6
# Authors:
7
#    Esben S. Nielsen
8
#    Thomas Lavergne
9
#    Adam Dybbroe <adam.dybbroe@smhi.se>
10
#
11
# This program is free software: you can redistribute it and/or modify it under
12
# the terms of the GNU Lesser General Public License as published by the Free
13
# Software Foundation, either version 3 of the License, or (at your option) any
14
# later version.
15
#
16
# This program is distributed in the hope that it will be useful, but WITHOUT
17
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18
# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
19
# details.
20
#
21
# You should have received a copy of the GNU Lesser General Public License
22
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
23
"""Utility functions for quick and easy display."""
4✔
24

25
from __future__ import absolute_import
4✔
26

27
import numpy as np
4✔
28

29
try:
4✔
30
    from pyresample.utils import cartopy  # noqa
4✔
31

32
    BASEMAP_NOT_CARTOPY = False
4✔
33
except ImportError:
×
34
    BASEMAP_NOT_CARTOPY = True
×
35

36

37
def ellps2axis(ellps_name):
4✔
38
    """Get semi-major and semi-minor axis from ellipsis definition.
39

40
    Parameters
41
    ---------
42
    ellps_name : str
43
        Standard name of ellipsis
44

45
    Returns
46
    -------
47
    (a, b) : semi-major and semi-minor axis
48
    """
49
    ellps = {'helmert': {'a': 6378200.0, 'b': 6356818.1696278909},
4✔
50
             'intl': {'a': 6378388.0, 'b': 6356911.9461279465},
51
             'merit': {'a': 6378137.0, 'b': 6356752.2982159676},
52
             'wgs72': {'a': 6378135.0, 'b': 6356750.5200160937},
53
             'sphere': {'a': 6370997.0, 'b': 6370997.0},
54
             'clrk66': {'a': 6378206.4000000004, 'b': 6356583.7999999998},
55
             'nwl9d': {'a': 6378145.0, 'b': 6356759.7694886839},
56
             'lerch': {'a': 6378139.0, 'b': 6356754.2915103417},
57
             'evrstss': {'a': 6377298.5559999999, 'b': 6356097.5503008962},
58
             'evrst30': {'a': 6377276.3449999997, 'b': 6356075.4131402401},
59
             'mprts': {'a': 6397300.0, 'b': 6363806.2827225132},
60
             'krass': {'a': 6378245.0, 'b': 6356863.0187730473},
61
             'walbeck': {'a': 6376896.0, 'b': 6355834.8466999996},
62
             'kaula': {'a': 6378163.0, 'b': 6356776.9920869097},
63
             'wgs66': {'a': 6378145.0, 'b': 6356759.7694886839},
64
             'evrst56': {'a': 6377301.2429999998, 'b': 6356100.2283681016},
65
             'new_intl': {'a': 6378157.5, 'b': 6356772.2000000002},
66
             'airy': {'a': 6377563.3959999997, 'b': 6356256.9100000001},
67
             'bessel': {'a': 6377397.1550000003, 'b': 6356078.9628181886},
68
             'seasia': {'a': 6378155.0, 'b': 6356773.3205000004},
69
             'aust_sa': {'a': 6378160.0, 'b': 6356774.7191953054},
70
             'wgs84': {'a': 6378137.0, 'b': 6356752.3142451793},
71
             'hough': {'a': 6378270.0, 'b': 6356794.3434343431},
72
             'wgs60': {'a': 6378165.0, 'b': 6356783.2869594367},
73
             'engelis': {'a': 6378136.0499999998, 'b': 6356751.3227215428},
74
             'apl4.9': {'a': 6378137.0, 'b': 6356751.796311819},
75
             'andrae': {'a': 6377104.4299999997, 'b': 6355847.4152333336},
76
             'sgs85': {'a': 6378136.0, 'b': 6356751.301568781},
77
             'delmbr': {'a': 6376428.0, 'b': 6355957.9261637237},
78
             'fschr60m': {'a': 6378155.0, 'b': 6356773.3204827355},
79
             'iau76': {'a': 6378140.0, 'b': 6356755.2881575283},
80
             'plessis': {'a': 6376523.0, 'b': 6355863.0},
81
             'cpm': {'a': 6375738.7000000002, 'b': 6356666.221912113},
82
             'fschr68': {'a': 6378150.0, 'b': 6356768.3372443849},
83
             'mod_airy': {'a': 6377340.1890000002, 'b': 6356034.4460000005},
84
             'grs80': {'a': 6378137.0, 'b': 6356752.3141403561},
85
             'bess_nam': {'a': 6377483.8650000002, 'b': 6356165.3829663256},
86
             'fschr60': {'a': 6378166.0, 'b': 6356784.2836071067},
87
             'clrk80': {'a': 6378249.1449999996, 'b': 6356514.9658284895},
88
             'evrst69': {'a': 6377295.6639999999, 'b': 6356094.6679152036},
89
             'grs67': {'a': 6378160.0, 'b': 6356774.5160907144},
90
             'evrst48': {'a': 6377304.0630000001, 'b': 6356103.0389931547}}
91
    try:
4✔
92
        ellps_axis = ellps[ellps_name.lower()]
4✔
93
        a = ellps_axis['a']
4✔
94
        b = ellps_axis['b']
4✔
95
    except KeyError:
×
96
        raise ValueError(('Could not determine semi-major and semi-minor axis '
×
97
                          'of specified ellipsis %s') % ellps_name)
98
    return a, b
4✔
99

100

101
def area_def2basemap(area_def, **kwargs):
4✔
102
    """Get Basemap object from an AreaDefinition object.
103

104
    Parameters
105
    ---------
106
    area_def : object
107
        geometry.AreaDefinition object
108
    **kwargs: Keyword arguments
109
        Additional initialization arguments for Basemap
110

111
    Returns
112
    -------
113
    bmap : Basemap object
114
    """
115
    import warnings
×
116
    warnings.warn("Basemap is no longer maintained. Please switch to cartopy "
×
117
                  "by using 'area_def.to_cartopy_crs()'. See the pyresample "
118
                  "documentation for more details.", DeprecationWarning, stacklevel=2)
119

120
    from mpl_toolkits.basemap import Basemap
×
121
    try:
×
122
        a, b = ellps2axis(area_def.proj_dict['ellps'])
×
123
        rsphere = (a, b)
×
124
    except KeyError:
×
125
        try:
×
126
            a = float(area_def.proj_dict['a'])
×
127
            try:
×
128
                b = float(area_def.proj_dict['b'])
×
129
                rsphere = (a, b)
×
130
            except KeyError:
×
131
                rsphere = a
×
132
        except KeyError:
×
133
            # Default to WGS84 ellipsoid
134
            a, b = ellps2axis('wgs84')
×
135
            rsphere = (a, b)
×
136

137
    # Add projection specific basemap args to args passed to function
138
    basemap_args = kwargs
×
139
    basemap_args['rsphere'] = rsphere
×
140

141
    if area_def.proj_dict['proj'] in ('ortho', 'geos', 'nsper'):
×
142
        llcrnrx, llcrnry, urcrnrx, urcrnry = area_def.area_extent
×
143
        basemap_args['llcrnrx'] = llcrnrx
×
144
        basemap_args['llcrnry'] = llcrnry
×
145
        basemap_args['urcrnrx'] = urcrnrx
×
146
        basemap_args['urcrnry'] = urcrnry
×
147
    else:
148
        llcrnrlon, llcrnrlat, urcrnrlon, urcrnrlat = area_def.area_extent_ll
×
149
        basemap_args['llcrnrlon'] = llcrnrlon
×
150
        basemap_args['llcrnrlat'] = llcrnrlat
×
151
        basemap_args['urcrnrlon'] = urcrnrlon
×
152
        basemap_args['urcrnrlat'] = urcrnrlat
×
153

154
    if area_def.proj_dict['proj'] == 'eqc':
×
155
        basemap_args['projection'] = 'cyl'
×
156
    else:
157
        basemap_args['projection'] = area_def.proj_dict['proj']
×
158

159
    # Try adding potentially remaining args
160
    for key in ('lon_0', 'lat_0', 'lon_1', 'lat_1', 'lon_2', 'lat_2',
×
161
                'lat_ts'):
162
        try:
×
163
            basemap_args[key] = float(area_def.proj_dict[key])
×
164
        except KeyError:
×
165
            pass
×
166

167
    return Basemap(**basemap_args)
×
168

169

170
def _basemap_get_quicklook(area_def, data, vmin=None, vmax=None,
4✔
171
                           label='Variable (units)', num_meridians=45,
172
                           num_parallels=10, coast_res='110m', cmap='RdBu_r'):
173
    """Doing quicklook image plots with Basemap."""
174
    if area_def.shape != data.shape:
×
175
        raise ValueError('area_def shape %s does not match data shape %s' %
×
176
                         (list(area_def.shape), list(data.shape)))
177
    import matplotlib.pyplot as plt
×
178
    bmap = area_def2basemap(area_def, resolution=coast_res)
×
179
    bmap.drawcoastlines()
×
180
    if num_meridians > 0:
×
181
        bmap.drawmeridians(np.arange(-180, 180, num_meridians))
×
182
    if num_parallels > 0:
×
183
        bmap.drawparallels(np.arange(-90, 90, num_parallels))
×
184
    if not (np.ma.isMaskedArray(data) and data.mask.all()):
×
185
        col = bmap.imshow(data, origin='upper', vmin=vmin, vmax=vmax, cmap=cmap)
×
186
        plt.colorbar(col, shrink=0.5, pad=0.05).set_label(label)
×
187
    return plt
×
188

189

190
def _translate_coast_resolution_to_cartopy(coast_res):
4✔
191
    """Translate the coast resolution argument between cartopy and basemap notation."""
192
    bmap_to_cartopy_res = {
4✔
193
        'c': '110m',
194
        'l': '110m',
195
        'i': '50m',
196
        'h': '10m',
197
        'f': '10m'
198
    }
199

200
    if BASEMAP_NOT_CARTOPY:
4✔
201
        if coast_res.endswith('m'):
×
202
            _rev_map = {v: k for k, v in bmap_to_cartopy_res.items()}
×
203
            coast_res = _rev_map[coast_res]
×
204
        return coast_res, False
×
205

206
    if coast_res and coast_res not in ['110m', '50m', '10m']:
4✔
207
        import warnings
4✔
208
        warnings.warn("'coast_res' should be either '110m', '50m', '10m'.", stacklevel=3)
4✔
209
        coast_res = {
4✔
210
            'c': '110m',
211
            'l': '110m',
212
            'i': '50m',
213
            'h': '10m',
214
            'f': '10m'
215
        }[coast_res]
216

217
    return coast_res, True
4✔
218

219

220
def _add_gridlines(axes, nmeridians, nparallels):
4✔
221
    """Add gridlines: meridians and parallels onto the plot."""
222
    from matplotlib import ticker as mticker
4✔
223

224
    gl = axes.gridlines()
4✔
225
    if nmeridians:
4✔
226
        gl.xlocator = mticker.FixedLocator(np.arange(-180, 180 + nmeridians, nmeridians))
4✔
227
    else:
228
        gl.xlines = False
4✔
229
    if nparallels:
4✔
230
        gl.ylocator = mticker.FixedLocator(np.arange(-90, 90 + nparallels, nparallels))
4✔
231
    else:
232
        gl.ylines = False
4✔
233

234
    return gl
4✔
235

236

237
def _get_quicklook(area_def, data, vmin=None, vmax=None,
4✔
238
                   label='Variable (units)', num_meridians=45,
239
                   num_parallels=10, coast_res='110m', cmap='RdBu_r'):
240
    """Get default cartopy matplotlib plot."""
241
    import matplotlib.pyplot as plt
4✔
242

243
    coast_res, is_cartopy = _translate_coast_resolution_to_cartopy(coast_res)
4✔
244
    if not is_cartopy:
4✔
245
        return _basemap_get_quicklook(
×
246
            area_def, data, vmin, vmax, label, num_meridians,
247
            num_parallels, coast_res=coast_res, cmap=cmap)
248

249
    if area_def.shape != data.shape:
4✔
250
        raise ValueError('area_def shape %s does not match data shape %s' %
×
251
                         (list(area_def.shape), list(data.shape)))
252

253
    crs = area_def.to_cartopy_crs()
4✔
254
    ax = plt.axes(projection=crs)
4✔
255
    ax.coastlines(resolution=coast_res)
4✔
256
    ax.set_global()
4✔
257

258
    if num_meridians or num_parallels:
4✔
259
        _ = _add_gridlines(ax, num_meridians, num_parallels)
4✔
260

261
    if not (np.ma.isMaskedArray(data) and data.mask.all()):
4✔
262
        col = ax.imshow(data, transform=crs, extent=crs.bounds,
4✔
263
                        origin='upper', vmin=vmin, vmax=vmax, cmap=cmap)
264
        plt.colorbar(col, shrink=0.5, pad=0.05).set_label(label)
4✔
265
    return plt
4✔
266

267

268
def show_quicklook(area_def, data, vmin=None, vmax=None,
4✔
269
                   label='Variable (units)', num_meridians=45,
270
                   num_parallels=10, coast_res='110m', cmap='RdBu_r'):
271
    """Display default quicklook plot.
272

273
    Parameters
274
    ---------
275
    area_def : object
276
        geometry.AreaDefinition object
277
    data : numpy array | numpy masked array
278
        2D array matching area_def. Use masked array for transparent values
279
    vmin : float, optional
280
        Min value for luminescence scaling
281
    vmax : float, optional
282
        Max value for luminescence scaling
283
    label : str, optional
284
        Label for data
285
    num_meridians : int, optional
286
        Number of meridians to plot on the globe
287
    num_parallels : int, optional
288
        Number of parallels to plot on the globe
289
    coast_res : {'c', 'l', 'i', 'h', 'f'}, optional
290
        Resolution of coastlines
291

292
    Returns
293
    -------
294
    bmap : Basemap object
295
    """
296
    plt = _get_quicklook(area_def, data, vmin=vmin, vmax=vmax,
×
297
                         label=label, num_meridians=num_meridians,
298
                         num_parallels=num_parallels, coast_res=coast_res,
299
                         cmap=cmap)
300
    plt.show()
×
301
    plt.close()
×
302

303

304
def save_quicklook(filename, area_def, data, vmin=None, vmax=None,
4✔
305
                   label='Variable (units)', num_meridians=45,
306
                   num_parallels=10, coast_res='110m',
307
                   cmap='RdBu_r'):
308
    """Display and save default quicklook plot.
309

310
    Parameters
311
    ----------
312
    filename : str
313
        path to output file
314
    area_def : object
315
        geometry.AreaDefinition object
316
    data : numpy array | numpy masked array
317
        2D array matching area_def. Use masked array for transparent values
318
    vmin : float, optional
319
        Min value for luminescence scaling
320
    vmax : float, optional
321
        Max value for luminescence scaling
322
    label : str, optional
323
        Label for data
324
    num_meridians : int, optional
325
        Number of meridians to plot on the globe
326
    num_parallels : int, optional
327
        Number of parallels to plot on the globe
328
    coast_res : {'c', 'l', 'i', 'h', 'f'}, optional
329
        Resolution of coastlines
330
    """
331
    plt = _get_quicklook(area_def, data, vmin=vmin, vmax=vmax,
×
332
                         label=label, num_meridians=num_meridians,
333
                         num_parallels=num_parallels, coast_res=coast_res,
334
                         cmap=cmap)
335
    plt.savefig(filename, bbox_inches='tight')
×
336
    plt.close()
×
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

© 2025 Coveralls, Inc