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

OGGM / oggm / 3836586642

pending completion
3836586642

Pull #1510

github

GitHub
Merge 2a19b5c72 into 8ab12c504
Pull Request #1510: Fix issue with TANDEM DEM and minor RGI7 issue

2 of 4 new or added lines in 2 files covered. (50.0%)

249 existing lines in 7 files now uncovered.

12099 of 13681 relevant lines covered (88.44%)

3.61 hits per line

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

89.48
/oggm/graphics.py
1
"""Useful plotting functions"""
2
import os
9✔
3
import functools
9✔
4
import logging
9✔
5
from collections import OrderedDict
9✔
6
import itertools
9✔
7
import textwrap
9✔
8
import xarray as xr
9✔
9

10
import matplotlib.colors as colors
9✔
11
import matplotlib.pyplot as plt
9✔
12
import numpy as np
9✔
13
import shapely.geometry as shpg
9✔
14
from matplotlib import cm as colormap
9✔
15

16
try:
9✔
17
    import salem
9✔
18
except ImportError:
19
    pass
20

21
OGGM_CMAPS = dict()
9✔
22

23
from oggm.core.flowline import FileModel
9✔
24
from oggm import cfg, utils
9✔
25
from oggm.core import gis
9✔
26

27
# Module logger
28
log = logging.getLogger(__name__)
9✔
29

30

31
def set_oggm_cmaps():
9✔
32
    # Set global colormaps
33
    global OGGM_CMAPS
34
    OGGM_CMAPS['terrain'] = colormap.terrain
9✔
35
    OGGM_CMAPS['section_thickness'] = plt.cm.get_cmap('YlGnBu')
9✔
36
    OGGM_CMAPS['glacier_thickness'] = plt.get_cmap('viridis')
9✔
37
    OGGM_CMAPS['ice_velocity'] = plt.cm.get_cmap('Reds')
9✔
38

39

40
set_oggm_cmaps()
9✔
41

42

43
def truncate_colormap(cmap, minval=0.0, maxval=1.0, n=256):
9✔
44
    """Remove extreme colors from a colormap."""
45
    new_cmap = colors.LinearSegmentedColormap.from_list(
1✔
46
        'trunc({n},{a:.2f},{b:.2f})'.format(n=cmap.name, a=minval, b=maxval),
47
        cmap(np.linspace(minval, maxval, n)))
48
    return new_cmap
1✔
49

50

51
def gencolor_generator(n, cmap='Set1'):
9✔
52
    """ Color generator intended to work with qualitative color scales."""
53
    # don't use more than 9 discrete colors
54
    n_colors = min(n, 9)
1✔
55
    cmap = colormap.get_cmap(cmap, n_colors)
1✔
56
    colors = cmap(range(n_colors))
1✔
57
    for i in range(n):
1✔
58
        yield colors[i % n_colors]
1✔
59

60

61
def gencolor(n, cmap='Set1'):
9✔
62

63
    if isinstance(cmap, str):
1✔
64
        return gencolor_generator(n, cmap=cmap)
1✔
65
    else:
66
        return itertools.cycle(cmap)
×
67

68

69
def surf_to_nan(surf_h, thick):
9✔
70

71
    t1 = thick[:-2]
1✔
72
    t2 = thick[1:-1]
1✔
73
    t3 = thick[2:]
1✔
74
    pnan = ((t1 == 0) & (t2 == 0)) & ((t2 == 0) & (t3 == 0))
1✔
75
    surf_h[np.where(pnan)[0] + 1] = np.NaN
1✔
76
    return surf_h
1✔
77

78

79
def combine_grids(gdirs):
9✔
80
    """ Combines individual grids of different glacier directories to show
81
        multiple glaciers in the same plot. The resulting grid extent includes
82
        all individual grids completely.
83

84
    Parameters
85
    ----------
86
    gdirs : [], required
87
        A list of GlacierDirectories.
88

89
    Returns
90
    -------
91
    salem.gis.Grid
92
    """
93

94
    new_grid = {
2✔
95
        'proj': None,
96
        'nxny': None,
97
        'dxdy': None,
98
        'x0y0': None,
99
        'pixel_ref': None
100
    }
101

102
    left_use = None
2✔
103
    right_use = None
2✔
104
    bottom_use = None
2✔
105
    top_use = None
2✔
106
    dx_use = None
2✔
107
    dy_use = None
2✔
108

109
    for gdir in gdirs:
2✔
110
        # use the first gdir to define some values
111
        if new_grid['proj'] is None:
2✔
112
            new_grid['proj'] = gdir.grid.proj
2✔
113
        if new_grid['pixel_ref'] is None:
2✔
114
            new_grid['pixel_ref'] = gdir.grid.pixel_ref
2✔
115

116
        # find largest extend including all grids completely
117
        (left, right, bottom, top) = gdir.grid.extent_in_crs(new_grid['proj'])
2✔
118
        if (left_use is None) or (left_use > left):
2✔
119
            left_use = left
2✔
120
        if right_use is None or right_use < right:
2✔
121
            right_use = right
2✔
122
        if bottom_use is None or bottom_use > bottom:
2✔
123
            bottom_use = bottom
2✔
124
        if top_use is None or top_use < top:
2✔
125
            top_use = top
2✔
126

127
        # find smallest dx and dy for the estimation of nx and ny
128
        dx = gdir.grid.dx
2✔
129
        dy = gdir.grid.dy
2✔
130
        if dx_use is None or dx_use > dx:
2✔
131
            dx_use = dx
2✔
132
        # dy could be negative
133
        if dy_use is None or abs(dy_use) > abs(dy):
2✔
134
            dy_use = dy
2✔
135

136
    # calculate nx and ny, the final extend could be one grid point larger or
137
    # smaller due to round()
138
    nx_use = round((right_use - left_use) / dx_use)
2✔
139
    ny_use = round((top_use - bottom_use) / abs(dy_use))
2✔
140

141
    # finally define the last values of the new grid
142
    if np.sign(dy_use) < 0:
2✔
143
        new_grid['x0y0'] = (left_use, top_use)
2✔
144
    else:
UNCOV
145
        new_grid['x0y0'] = (left_use, bottom_use)
×
146
    new_grid['nxny'] = (nx_use, ny_use)
2✔
147
    new_grid['dxdy'] = (dx_use, dy_use)
2✔
148

149
    return salem.gis.Grid.from_dict(new_grid)
2✔
150

151

152
def _plot_map(plotfunc):
9✔
153
    """
154
    Decorator for common salem.Map plotting logic
155
    """
156
    commondoc = """
9✔
157

158
    Parameters
159
    ----------
160
    gdirs : [] or GlacierDirectory, required
161
        A single GlacierDirectory or a list of gdirs to plot.
162
    ax : matplotlib axes object, optional
163
        If None, uses own axis
164
    smap : Salem Map object, optional
165
        If None, makes a map from the first gdir in the list
166
    add_scalebar : Boolean, optional, default=True
167
        Adds scale bar to the plot
168
    add_colorbar : Boolean, optional, default=True
169
        Adds colorbar to axis
170
    horizontal_colorbar : Boolean, optional, default=False
171
        Horizontal colorbar instead
172
    title : str, optional
173
        If left to None, the plot decides whether it writes a title or not. Set
174
        to '' for no title.
175
    title_comment : str, optional
176
        add something to the default title. Set to none to remove default
177
    lonlat_contours_kwargs: dict, optional
178
        pass kwargs to salem.Map.set_lonlat_contours
179
    cbar_ax: ax, optional
180
        ax where to plot the colorbar
181
    autosave : bool, optional
182
        set to True to override to a default savefig filename (useful
183
        for multiprocessing)
184
    figsize : tuple, optional
185
        size of the figure
186
    savefig : str, optional
187
        save the figure to a file instead of displaying it
188
    savefig_kwargs : dict, optional
189
        the kwargs to plt.savefig
190
    extend_plot_limit : bool, optional
191
        set to True to extend the plotting limits for all provided gdirs grids
192
    """
193

194
    # Build on the original docstring
195
    plotfunc.__doc__ = '\n'.join((plotfunc.__doc__, commondoc))
9✔
196

197
    @functools.wraps(plotfunc)
9✔
198
    def newplotfunc(gdirs, ax=None, smap=None, add_colorbar=True, title=None,
9✔
199
                    title_comment=None, horizontal_colorbar=False,
200
                    lonlat_contours_kwargs=None, cbar_ax=None, autosave=False,
201
                    add_scalebar=True, figsize=None, savefig=None,
202
                    savefig_kwargs=None, extend_plot_limit=False,
203
                    **kwargs):
204

205
        dofig = False
3✔
206
        if ax is None:
3✔
207
            fig = plt.figure(figsize=figsize)
×
UNCOV
208
            ax = fig.add_subplot(111)
×
UNCOV
209
            dofig = True
×
210

211
        # Cast to list
212
        gdirs = utils.tolist(gdirs)
3✔
213

214
        if smap is None:
3✔
215
            if extend_plot_limit:
3✔
216
                grid_combined = combine_grids(gdirs)
2✔
217
                mp = salem.Map(grid_combined, countries=False,
2✔
218
                               nx=grid_combined.nx)
219
            else:
220
                mp = salem.Map(gdirs[0].grid, countries=False,
1✔
221
                               nx=gdirs[0].grid.nx)
222
        else:
223
            mp = smap
3✔
224

225
        if lonlat_contours_kwargs is not None:
3✔
UNCOV
226
            mp.set_lonlat_contours(**lonlat_contours_kwargs)
×
227

228
        if add_scalebar:
3✔
229
            mp.set_scale_bar()
3✔
230
        out = plotfunc(gdirs, ax=ax, smap=mp, **kwargs)
3✔
231

232
        if add_colorbar and 'cbar_label' in out:
3✔
233
            cbprim = out.get('cbar_primitive', mp)
3✔
234
            if cbar_ax:
3✔
235
                cb = cbprim.colorbarbase(cbar_ax)
×
236
            else:
237
                if horizontal_colorbar:
3✔
UNCOV
238
                    cb = cbprim.append_colorbar(ax, "bottom", size="5%",
×
239
                                                pad=0.4)
240
                else:
241
                    cb = cbprim.append_colorbar(ax, "right", size="5%",
3✔
242
                                                pad=0.2)
243
            cb.set_label(out['cbar_label'])
3✔
244

245
        if title is None:
3✔
246
            if 'title' not in out:
3✔
247
                # Make a default one
248
                title = ''
3✔
249
                if len(gdirs) == 1:
3✔
250
                    gdir = gdirs[0]
1✔
251
                    title = gdir.rgi_id
1✔
252
                    if gdir.name is not None and gdir.name != '':
1✔
253
                        title += ': ' + gdir.name
1✔
254
                out['title'] = title
3✔
255

256
            if title_comment is None:
3✔
257
                title_comment = out.get('title_comment', '')
3✔
258

259
            out['title'] += title_comment
3✔
260
            ax.set_title(out['title'])
3✔
261
        else:
UNCOV
262
            ax.set_title(title)
×
263

264
        if dofig:
3✔
UNCOV
265
            plt.tight_layout()
×
266

267
        if autosave:
3✔
268
            savefig = os.path.join(cfg.PATHS['working_dir'], 'plots')
×
269
            utils.mkdir(savefig)
×
UNCOV
270
            savefig = os.path.join(savefig, plotfunc.__name__ + '_' +
×
271
                                   gdirs[0].rgi_id + '.png')
272

273
        if savefig is not None:
3✔
UNCOV
274
            plt.savefig(savefig, **savefig_kwargs)
×
UNCOV
275
            plt.close()
×
276

277
    return newplotfunc
9✔
278

279

280
def plot_googlemap(gdirs, ax=None, figsize=None):
9✔
281
    """Plots the glacier(s) over a googlemap."""
282

283
    dofig = False
1✔
284
    if ax is None:
1✔
UNCOV
285
        fig = plt.figure(figsize=figsize)
×
UNCOV
286
        ax = fig.add_subplot(111)
×
UNCOV
287
        dofig = True
×
288

289
    gdirs = utils.tolist(gdirs)
1✔
290

291
    xx, yy = [], []
1✔
292
    for gdir in gdirs:
1✔
293
        xx.extend(gdir.extent_ll[0])
1✔
294
        yy.extend(gdir.extent_ll[1])
1✔
295

296
    gm = salem.GoogleVisibleMap(xx, yy,
1✔
297
                                key='AIzaSyDWG_aTgfU7CeErtIzWfdGxpStTlvDXV_o')
298

299
    img = gm.get_vardata()
1✔
300
    cmap = salem.Map(gm.grid, countries=False, nx=gm.grid.nx)
1✔
301
    cmap.set_rgb(img)
1✔
302

303
    for gdir in gdirs:
1✔
304
        cmap.set_shapefile(gdir.read_shapefile('outlines'))
1✔
305

306
    cmap.plot(ax)
1✔
307
    title = ''
1✔
308
    if len(gdirs) == 1:
1✔
309
        title = gdir.rgi_id
1✔
310
        if gdir.name is not None and gdir.name != '':
1✔
311
            title += ': ' + gdir.name
1✔
312
    ax.set_title(title)
1✔
313

314
    if dofig:
1✔
UNCOV
315
        plt.tight_layout()
×
316

317

318
@_plot_map
9✔
319
def plot_raster(gdirs, var_name=None, cmap='viridis', ax=None, smap=None):
9✔
320
    """Plot any raster from the gridded_data file."""
321

322
    # Files
323
    gdir = gdirs[0]
1✔
324

325
    with utils.ncDataset(gdir.get_filepath('gridded_data')) as nc:
1✔
326
        var = nc.variables[var_name]
1✔
327
        data = var[:]
1✔
328
        description = var.long_name
1✔
329
        description += ' [{}]'.format(var.units)
1✔
330

331
    smap.set_data(data)
1✔
332

333
    smap.set_cmap(cmap)
1✔
334

335
    for gdir in gdirs:
1✔
336
        crs = gdir.grid.center_grid
1✔
337

338
        try:
1✔
339
            geom = gdir.read_pickle('geometries')
1✔
340
            # Plot boundaries
341
            poly_pix = geom['polygon_pix']
1✔
342
            smap.set_geometry(poly_pix, crs=crs, fc='none',
1✔
343
                              alpha=0.3, zorder=2, linewidth=.2)
344
            poly_pix = utils.tolist(poly_pix)
1✔
345
            for _poly in poly_pix:
1✔
346
                for l in _poly.interiors:
1✔
347
                    smap.set_geometry(l, crs=crs, color='black', linewidth=0.5)
1✔
UNCOV
348
        except FileNotFoundError:
×
UNCOV
349
            smap.set_shapefile(gdir.read_shapefile('outlines'))
×
350

351
    smap.plot(ax)
1✔
352

353
    return dict(cbar_label='\n'.join(textwrap.wrap(description, 30)))
1✔
354

355

356
@_plot_map
9✔
357
def plot_domain(gdirs, ax=None, smap=None, use_netcdf=False):
9✔
358
    """Plot the glacier directory.
359

360
    Parameters
361
    ----------
362
    gdirs
363
    ax
364
    smap
365
    use_netcdf : bool
366
        use output of glacier_masks instead of geotiff DEM
367
    """
368

369
    # Files
370
    gdir = gdirs[0]
1✔
371
    if use_netcdf:
1✔
UNCOV
372
        with utils.ncDataset(gdir.get_filepath('gridded_data')) as nc:
×
UNCOV
373
            topo = nc.variables['topo'][:]
×
374
    else:
375
        topo = gis.read_geotiff_dem(gdir)
1✔
376
    try:
1✔
377
        smap.set_data(topo)
1✔
UNCOV
378
    except ValueError:
×
UNCOV
379
        pass
×
380

381
    cm = truncate_colormap(OGGM_CMAPS['terrain'], minval=0.25, maxval=1.0)
1✔
382
    smap.set_plot_params(cmap=cm)
1✔
383

384
    for gdir in gdirs:
1✔
385
        crs = gdir.grid.center_grid
1✔
386

387
        try:
1✔
388
            geom = gdir.read_pickle('geometries')
1✔
389

390
            # Plot boundaries
391
            poly_pix = geom['polygon_pix']
1✔
392
            smap.set_geometry(poly_pix, crs=crs, fc='white',
1✔
393
                              alpha=0.3, zorder=2, linewidth=.2)
394
            poly_pix = utils.tolist(poly_pix)
1✔
395
            for _poly in poly_pix:
1✔
396
                for l in _poly.interiors:
1✔
397
                    smap.set_geometry(l, crs=crs, color='black', linewidth=0.5)
1✔
UNCOV
398
        except FileNotFoundError:
×
UNCOV
399
            smap.set_shapefile(gdir.read_shapefile('outlines'))
×
400

401
    smap.plot(ax)
1✔
402

403
    return dict(cbar_label='Alt. [m]')
1✔
404

405

406
@_plot_map
9✔
407
def plot_centerlines(gdirs, ax=None, smap=None, use_flowlines=False,
9✔
408
                     add_downstream=False, lines_cmap='Set1',
409
                     add_line_index=False, use_model_flowlines=False):
410
    """Plots the centerlines of a glacier directory."""
411

412
    if add_downstream and not use_flowlines:
1✔
UNCOV
413
        raise ValueError('Downstream lines can be plotted with flowlines only')
×
414

415
    # Files
416
    filename = 'centerlines'
1✔
417
    if use_model_flowlines:
1✔
UNCOV
418
        filename = 'model_flowlines'
×
419
    elif use_flowlines:
1✔
420
        filename = 'inversion_flowlines'
1✔
421

422
    gdir = gdirs[0]
1✔
423
    with utils.ncDataset(gdir.get_filepath('gridded_data')) as nc:
1✔
424
        topo = nc.variables['topo'][:]
1✔
425

426
    cm = truncate_colormap(OGGM_CMAPS['terrain'], minval=0.25, maxval=1.0)
1✔
427
    smap.set_plot_params(cmap=cm)
1✔
428
    smap.set_data(topo)
1✔
429
    for gdir in gdirs:
1✔
430
        crs = gdir.grid.center_grid
1✔
431
        geom = gdir.read_pickle('geometries')
1✔
432

433
        # Plot boundaries
434
        poly_pix = geom['polygon_pix']
1✔
435

436
        smap.set_geometry(poly_pix, crs=crs, fc='white',
1✔
437
                          alpha=0.3, zorder=2, linewidth=.2)
438
        poly_pix = utils.tolist(poly_pix)
1✔
439
        for _poly in poly_pix:
1✔
440
            for l in _poly.interiors:
1✔
441
                smap.set_geometry(l, crs=crs, color='black', linewidth=0.5)
1✔
442

443
        # plot Centerlines
444
        cls = gdir.read_pickle(filename)
1✔
445

446
        # Go in reverse order for red always being the longest
447
        cls = cls[::-1]
1✔
448
        nl = len(cls)
1✔
449
        color = gencolor(len(cls) + 1, cmap=lines_cmap)
1✔
450
        for i, (l, c) in enumerate(zip(cls, color)):
1✔
451
            if add_downstream and not gdir.is_tidewater and l is cls[0]:
1✔
452
                line = gdir.read_pickle('downstream_line')['full_line']
1✔
453
            else:
454
                line = l.line
1✔
455

456
            smap.set_geometry(line, crs=crs, color=c,
1✔
457
                              linewidth=2.5, zorder=50)
458

459
            text = '{}'.format(nl - i - 1) if add_line_index else None
1✔
460
            smap.set_geometry(l.head, crs=gdir.grid, marker='o',
1✔
461
                              markersize=60, alpha=0.8, color=c, zorder=99,
462
                              text=text)
463

464
            for j in l.inflow_points:
1✔
465
                smap.set_geometry(j, crs=crs, marker='o',
1✔
466
                                  markersize=40, edgecolor='k', alpha=0.8,
467
                                  zorder=99, facecolor='none')
468

469
    smap.plot(ax)
1✔
470
    return dict(cbar_label='Alt. [m]')
1✔
471

472

473
@_plot_map
9✔
474
def plot_catchment_areas(gdirs, ax=None, smap=None, lines_cmap='Set1',
9✔
475
                         mask_cmap='Set2'):
476
    """Plots the catchments out of a glacier directory.
477
    """
478

479
    gdir = gdirs[0]
1✔
480
    if len(gdirs) > 1:
1✔
481
        raise NotImplementedError('Cannot plot a list of gdirs (yet)')
482

483
    with utils.ncDataset(gdir.get_filepath('gridded_data')) as nc:
1✔
484
        topo = nc.variables['topo'][:]
1✔
485
        mask = nc.variables['glacier_mask'][:] * np.NaN
1✔
486

487
    smap.set_topography(topo)
1✔
488

489
    crs = gdir.grid.center_grid
1✔
490
    geom = gdir.read_pickle('geometries')
1✔
491

492
    # Plot boundaries
493
    poly_pix = geom['polygon_pix']
1✔
494
    smap.set_geometry(poly_pix, crs=crs, fc='none', zorder=2,
1✔
495
                      linewidth=.2)
496
    for l in poly_pix.interiors:
1✔
497
        smap.set_geometry(l, crs=crs, color='black', linewidth=0.5)
1✔
498

499
    # plot Centerlines
500
    cls = gdir.read_pickle('centerlines')[::-1]
1✔
501
    color = gencolor(len(cls) + 1, cmap=lines_cmap)
1✔
502
    for l, c in zip(cls, color):
1✔
503
        smap.set_geometry(l.line, crs=crs, color=c,
1✔
504
                          linewidth=2.5, zorder=50)
505

506
    # catchment areas
507
    cis = gdir.read_pickle('geometries')['catchment_indices']
1✔
508
    for j, ci in enumerate(cis[::-1]):
1✔
509
        mask[tuple(ci.T)] = j+1
1✔
510

511
    smap.set_cmap(mask_cmap)
1✔
512
    smap.set_data(mask)
1✔
513
    smap.plot(ax)
1✔
514

515
    return {}
1✔
516

517

518
@_plot_map
9✔
519
def plot_catchment_width(gdirs, ax=None, smap=None, corrected=False,
9✔
520
                         add_intersects=False, add_touches=False,
521
                         lines_cmap='Set1'):
522
    """Plots the catchment widths out of a glacier directory.
523
    """
524

525
    gdir = gdirs[0]
1✔
526
    with utils.ncDataset(gdir.get_filepath('gridded_data')) as nc:
1✔
527
        topo = nc.variables['topo'][:]
1✔
528
    # Dirty optim
529
    try:
1✔
530
        smap.set_topography(topo)
1✔
531
    except ValueError:
1✔
532
        pass
1✔
533

534
    # Maybe plot touches
535
    xis, yis, cis = [], [], []
1✔
536
    ogrid = smap.grid
1✔
537

538
    for gdir in gdirs:
1✔
539
        crs = gdir.grid.center_grid
1✔
540
        geom = gdir.read_pickle('geometries')
1✔
541

542
        # Plot boundaries
543
        poly_pix = geom['polygon_pix']
1✔
544
        smap.set_geometry(poly_pix, crs=crs, fc='none', zorder=2,
1✔
545
                          linewidth=.2)
546
        for l in poly_pix.interiors:
1✔
547
            smap.set_geometry(l, crs=crs, color='black', linewidth=0.5)
1✔
548

549
        # Plot intersects
550
        if add_intersects and gdir.has_file('intersects'):
1✔
551
            gdf = gdir.read_shapefile('intersects')
1✔
552
            smap.set_shapefile(gdf, color='k', linewidth=3.5, zorder=3)
1✔
553

554
        # plot Centerlines
555
        cls = gdir.read_pickle('inversion_flowlines')[::-1]
1✔
556
        color = gencolor(len(cls) + 1, cmap=lines_cmap)
1✔
557
        for l, c in zip(cls, color):
1✔
558
            smap.set_geometry(l.line, crs=crs, color=c,
1✔
559
                              linewidth=2.5, zorder=50)
560
            if corrected:
1✔
561
                for wi, cur, (n1, n2) in zip(l.widths, l.line.coords,
1✔
562
                                             l.normals):
563
                    _l = shpg.LineString([shpg.Point(cur + wi / 2. * n1),
1✔
564
                                          shpg.Point(cur + wi / 2. * n2)])
565

566
                    smap.set_geometry(_l, crs=crs, color=c,
1✔
567
                                      linewidth=0.6, zorder=50)
568
            else:
569
                for wl, wi in zip(l.geometrical_widths, l.widths):
1✔
570
                    col = c if np.isfinite(wi) else 'grey'
1✔
571
                    for w in wl.geoms:
1✔
572
                        smap.set_geometry(w, crs=crs, color=col,
1✔
573
                                          linewidth=0.6, zorder=50)
574

575
            if add_touches:
1✔
576
                pok = np.where(l.is_rectangular)
1✔
577
                xi, yi = l.line.xy
1✔
578
                xi, yi = ogrid.transform(np.asarray(xi)[pok],
1✔
579
                                         np.asarray(yi)[pok], crs=crs)
580
                xis.append(xi)
1✔
581
                yis.append(yi)
1✔
582
                cis.append(c)
1✔
583

584
    smap.plot(ax)
1✔
585
    for xi, yi, c in zip(xis, yis, cis):
1✔
586
        ax.scatter(xi, yi, color=c, s=20, zorder=51)
1✔
587

588
    return {}
1✔
589

590

591
@_plot_map
9✔
592
def plot_inversion(gdirs, ax=None, smap=None, linewidth=3, vmax=None,
9✔
593
                   plot_var='thick', cbar_label='Section thickness (m)',
594
                   color_map='YlGnBu'):
595
    """Plots the result of the inversion out of a glacier directory.
596
       Default is thickness (m). Change plot_var to u_surface or u_integrated
597
       for velocity (m/yr)."""
598

599
    gdir = gdirs[0]
3✔
600
    with utils.ncDataset(gdir.get_filepath('gridded_data')) as nc:
3✔
601
        topo = nc.variables['topo'][:]
3✔
602

603
    # Dirty optim
604
    try:
3✔
605
        smap.set_topography(topo)
3✔
606
    except ValueError:
2✔
607
        pass
2✔
608

609
    toplot_var = np.array([])
3✔
610
    toplot_lines = []
3✔
611
    toplot_crs = []
3✔
612
    vol = []
3✔
613
    for gdir in gdirs:
3✔
614
        crs = gdir.grid.center_grid
3✔
615
        geom = gdir.read_pickle('geometries')
3✔
616
        inv = gdir.read_pickle('inversion_output')
3✔
617
        # Plot boundaries
618
        poly_pix = geom['polygon_pix']
3✔
619
        smap.set_geometry(poly_pix, crs=crs, fc='none', zorder=2,
3✔
620
                          linewidth=.2)
621
        for l in poly_pix.interiors:
3✔
622
            smap.set_geometry(l, crs=crs, color='black', linewidth=0.5)
3✔
623

624
        # Plot Centerlines
625
        cls = gdir.read_pickle('inversion_flowlines')
3✔
626
        for l, c in zip(cls, inv):
3✔
627

628
            smap.set_geometry(l.line, crs=crs, color='gray',
3✔
629
                              linewidth=1.2, zorder=50)
630

631
            toplot_var = np.append(toplot_var, c[plot_var])
3✔
632
            for wi, cur, (n1, n2) in zip(l.widths, l.line.coords, l.normals):
3✔
633
                line = shpg.LineString([shpg.Point(cur + wi / 2. * n1),
3✔
634
                                        shpg.Point(cur + wi / 2. * n2)])
635
                toplot_lines.append(line)
3✔
636
                toplot_crs.append(crs)
3✔
637
            vol.extend(c['volume'])
3✔
638

639
    dl = salem.DataLevels(cmap=plt.get_cmap(color_map),
3✔
640
                          data=toplot_var, vmin=0, vmax=vmax)
641
    colors = dl.to_rgb()
3✔
642
    for l, c, crs in zip(toplot_lines, colors, toplot_crs):
3✔
643
        smap.set_geometry(l, crs=crs, color=c,
3✔
644
                          linewidth=linewidth, zorder=50)
645

646
    smap.plot(ax)
3✔
647
    out = dict(cbar_label=cbar_label,
3✔
648
                cbar_primitive=dl)
649

650
    if plot_var == 'thick':
3✔
651
        out['title_comment'] = ' ({:.2f} km3)'.format(np.nansum(vol) * 1e-9)
3✔
652

653
    return out
3✔
654

655

656
@_plot_map
9✔
657
def plot_distributed_thickness(gdirs, ax=None, smap=None, varname_suffix=''):
9✔
658
    """Plots the result of the inversion out of a glacier directory.
659

660
    Method: 'alt' or 'interp'
661
    """
662

663
    gdir = gdirs[0]
1✔
664

665
    with utils.ncDataset(gdir.get_filepath('gridded_data')) as nc:
1✔
666
        topo = nc.variables['topo'][:]
1✔
667

668
    smap.set_topography(topo)
1✔
669

670
    for gdir in gdirs:
1✔
671
        grids_file = gdir.get_filepath('gridded_data')
1✔
672
        with utils.ncDataset(grids_file) as nc:
1✔
673
            import warnings
1✔
674
            with warnings.catch_warnings():
1✔
675
                # https://github.com/Unidata/netcdf4-python/issues/766
676
                warnings.filterwarnings("ignore", category=RuntimeWarning)
1✔
677
                vn = 'distributed_thickness' + varname_suffix
1✔
678
                thick = nc.variables[vn][:]
1✔
679
                mask = nc.variables['glacier_mask'][:]
1✔
680

681
        thick = np.where(mask, thick, np.NaN)
1✔
682

683
        crs = gdir.grid.center_grid
1✔
684

685
        # Plot boundaries
686
        # Try to read geometries.pkl as the glacier boundary,
687
        # if it can't be found, we use the shapefile to instead.
688
        try:
1✔
689
            geom = gdir.read_pickle('geometries')
1✔
690
            poly_pix = geom['polygon_pix']
1✔
691
            smap.set_geometry(poly_pix, crs=crs, fc='none', zorder=2, linewidth=.2)
1✔
692
            for l in poly_pix.interiors:
1✔
693
                smap.set_geometry(l, crs=crs, color='black', linewidth=0.5)
1✔
694
        except FileNotFoundError:
1✔
695
            smap.set_shapefile(gdir.read_shapefile('outlines'), fc='none')
1✔
696
        smap.set_data(thick, crs=crs, overplot=True)
1✔
697

698
    smap.set_plot_params(cmap=OGGM_CMAPS['glacier_thickness'])
1✔
699
    smap.plot(ax)
1✔
700

701
    return dict(cbar_label='Glacier thickness [m]')
1✔
702

703

704
@_plot_map
9✔
705
def plot_modeloutput_map(gdirs, ax=None, smap=None, model=None,
9✔
706
                         vmax=None, linewidth=3, filesuffix='',
707
                         modelyr=None, plotting_var='thickness'):
708
    """Plots the result of the model output.
709

710
    Parameters
711
    ----------
712
    gdirs
713
    ax
714
    smap
715
    model
716
    vmax
717
    linewidth
718
    filesuffix
719
    modelyr
720
    plotting_var : str
721
        Defines which variable should be plotted. Options are 'thickness'
722
        (default) and 'velocity'. If you want to plot velocity the flowline
723
        diagnostics of the run are needed (set
724
        cfg.PARAMS['store_fl_diagnostics'] = True, before the
725
        actual simulation) and be aware that there is no velocity available for
726
        the first year of the simulation.
727

728
    Returns
729
    -------
730

731
    """
732

733
    gdir = gdirs[0]
3✔
734
    with utils.ncDataset(gdir.get_filepath('gridded_data')) as nc:
3✔
735
        topo = nc.variables['topo'][:]
3✔
736

737
    # Dirty optim
738
    try:
3✔
739
        smap.set_topography(topo)
3✔
740
    except ValueError:
2✔
741
        pass
2✔
742

743
    toplot_var = np.array([])
3✔
744
    toplot_lines = []
3✔
745
    toplot_crs = []
3✔
746

747
    if model is None:
3✔
748
        models = []
2✔
749
        for gdir in gdirs:
2✔
750
            model = FileModel(gdir.get_filepath('model_geometry',
2✔
751
                                                filesuffix=filesuffix))
752
            model.run_until(modelyr)
2✔
753
            models.append(model)
2✔
754
    else:
755
        models = utils.tolist(model)
1✔
756

757
    if modelyr is None:
3✔
758
        modelyr = models[0].yr
1✔
759

760
    for gdir, model in zip(gdirs, models):
3✔
761
        geom = gdir.read_pickle('geometries')
3✔
762
        poly_pix = geom['polygon_pix']
3✔
763

764
        crs = gdir.grid.center_grid
3✔
765
        smap.set_geometry(poly_pix, crs=crs, fc='none', zorder=2, linewidth=.2)
3✔
766

767
        poly_pix = utils.tolist(poly_pix)
3✔
768
        for _poly in poly_pix:
3✔
769
            for l in _poly.interiors:
3✔
770
                smap.set_geometry(l, crs=crs, color='black', linewidth=0.5)
3✔
771

772
        if plotting_var == 'velocity':
3✔
UNCOV
773
            f_fl_diag = gdir.get_filepath('fl_diagnostics',
×
774
                                          filesuffix=filesuffix)
775

776
        # plot Centerlines
777
        cls = model.fls
3✔
778
        for fl_id, l in enumerate(cls):
3✔
779
            smap.set_geometry(l.line, crs=crs, color='gray',
3✔
780
                              linewidth=1.2, zorder=50)
781
            if plotting_var == 'thickness':
3✔
782
                toplot_var = np.append(toplot_var, l.thick)
3✔
UNCOV
783
            elif plotting_var == 'velocity':
×
UNCOV
784
                with xr.open_dataset(f_fl_diag, group=f'fl_{fl_id}') as ds:
×
UNCOV
785
                    toplot_var = np.append(toplot_var,
×
786
                                           ds.sel(dict(time=modelyr)).ice_velocity_myr)
787
            widths = l.widths.copy()
3✔
788
            widths = np.where(l.thick > 0, widths, 0.)
3✔
789
            for wi, cur, (n1, n2) in zip(widths, l.line.coords, l.normals):
3✔
790
                line = shpg.LineString([shpg.Point(cur + wi/2. * n1),
3✔
791
                                        shpg.Point(cur + wi/2. * n2)])
792
                toplot_lines.append(line)
3✔
793
                toplot_crs.append(crs)
3✔
794

795
    if plotting_var == 'thickness':
3✔
796
        cmap = OGGM_CMAPS['section_thickness']
3✔
797
        cbar_label = 'Section thickness [m]'
3✔
UNCOV
798
    elif plotting_var == 'velocity':
×
UNCOV
799
        cmap = OGGM_CMAPS['ice_velocity']
×
UNCOV
800
        cbar_label = 'Ice velocity [m yr-1]'
×
801
    dl = salem.DataLevels(cmap=cmap,
3✔
802
                          data=toplot_var, vmin=0, vmax=vmax)
803
    colors = dl.to_rgb()
3✔
804
    for l, c, crs in zip(toplot_lines, colors, toplot_crs):
3✔
805
        smap.set_geometry(l, crs=crs, color=c,
3✔
806
                          linewidth=linewidth, zorder=50)
807
    smap.plot(ax)
3✔
808
    return dict(cbar_label=cbar_label,
3✔
809
                cbar_primitive=dl,
810
                title_comment=' -- year: {:d}'.format(np.int64(model.yr)))
811

812

813
def plot_modeloutput_section(model=None, ax=None, title=''):
9✔
814
    """Plots the result of the model output along the flowline.
815

816
    Parameters
817
    ----------
818
    model: obj
819
        either a FlowlineModel or a list of model flowlines.
820
    fig
821
    title
822
    """
823

824
    try:
1✔
825
        fls = model.fls
1✔
UNCOV
826
    except AttributeError:
×
UNCOV
827
        fls = model
×
828

829
    if ax is None:
1✔
UNCOV
830
        fig = plt.figure(figsize=(12, 6))
×
UNCOV
831
        ax = fig.add_axes([0.07, 0.08, 0.7, 0.84])
×
832
    else:
833
        fig = plt.gcf()
1✔
834

835
    # Compute area histo
836
    area = np.array([])
1✔
837
    height = np.array([])
1✔
838
    bed = np.array([])
1✔
839
    for cls in fls:
1✔
840
        a = cls.widths_m * cls.dx_meter * 1e-6
1✔
841
        a = np.where(cls.thick > 0, a, 0)
1✔
842
        area = np.concatenate((area, a))
1✔
843
        height = np.concatenate((height, cls.surface_h))
1✔
844
        bed = np.concatenate((bed, cls.bed_h))
1✔
845
    ylim = [bed.min(), height.max()]
1✔
846

847
    # Plot histo
848
    posax = ax.get_position()
1✔
849
    posax = [posax.x0 + 2 * posax.width / 3.0,
1✔
850
             posax.y0,  posax.width / 3.0,
851
             posax.height]
852
    axh = fig.add_axes(posax, frameon=False)
1✔
853

854
    axh.hist(height, orientation='horizontal', range=ylim, bins=20,
1✔
855
             alpha=0.3, weights=area)
856
    axh.invert_xaxis()
1✔
857
    axh.xaxis.tick_top()
1✔
858
    axh.set_xlabel('Area incl. tributaries (km$^2$)')
1✔
859
    axh.xaxis.set_label_position('top')
1✔
860
    axh.set_ylim(ylim)
1✔
861
    axh.yaxis.set_ticks_position('right')
1✔
862
    axh.set_yticks([])
1✔
863
    axh.axhline(y=ylim[1], color='black', alpha=1)  # qick n dirty trick
1✔
864

865
    # plot Centerlines
866
    cls = fls[-1]
1✔
867
    x = np.arange(cls.nx) * cls.dx * cls.map_dx
1✔
868

869
    # Plot the bed
870
    ax.plot(x, cls.bed_h, color='k', linewidth=2.5, label='Bed (Parab.)')
1✔
871

872
    # Where trapezoid change color
873
    if hasattr(cls, '_do_trapeze') and cls._do_trapeze:
1✔
874
        bed_t = cls.bed_h * np.NaN
1✔
875
        pt = cls.is_trapezoid & (~cls.is_rectangular)
1✔
876
        bed_t[pt] = cls.bed_h[pt]
1✔
877
        ax.plot(x, bed_t, color='rebeccapurple', linewidth=2.5,
1✔
878
                label='Bed (Trap.)')
879
        bed_t = cls.bed_h * np.NaN
1✔
880
        bed_t[cls.is_rectangular] = cls.bed_h[cls.is_rectangular]
1✔
881
        ax.plot(x, bed_t, color='crimson', linewidth=2.5, label='Bed (Rect.)')
1✔
882

883
    # Plot glacier
884
    surfh = surf_to_nan(cls.surface_h, cls.thick)
1✔
885
    ax.plot(x, surfh, color='#003399', linewidth=2, label='Glacier')
1✔
886

887
    # Plot tributaries
888
    for i, l in zip(cls.inflow_indices, cls.inflows):
1✔
889
        if l.thick[-1] > 0:
1✔
890
            ax.plot(x[i], cls.surface_h[i], 's', markerfacecolor='#993399',
1✔
891
                    markeredgecolor='k',
892
                    label='Tributary (active)')
893
        else:
UNCOV
894
            ax.plot(x[i], cls.surface_h[i], 's', markerfacecolor='w',
×
895
                    markeredgecolor='k',
896
                    label='Tributary (inactive)')
897
    if getattr(model, 'do_calving', False):
1✔
UNCOV
898
        ax.hlines(model.water_level, x[0], x[-1], linestyles=':', color='C0')
×
899

900
    ax.set_ylim(ylim)
1✔
901

902
    ax.spines['top'].set_color('none')
1✔
903
    ax.xaxis.set_ticks_position('bottom')
1✔
904
    ax.set_xlabel('Distance along flowline (m)')
1✔
905
    ax.set_ylabel('Altitude (m)')
1✔
906

907
    # Title
908
    ax.set_title(title, loc='left')
1✔
909

910
    # Legend
911
    handles, labels = ax.get_legend_handles_labels()
1✔
912
    by_label = OrderedDict(zip(labels, handles))
1✔
913
    ax.legend(list(by_label.values()), list(by_label.keys()),
1✔
914
              bbox_to_anchor=(1.34, 1.0),
915
              frameon=False)
916

917

918
def plot_modeloutput_section_withtrib(model=None, fig=None, title=''):
9✔
919
    """Plots the result of the model output along the flowline.
920

921
    Parameters
922
    ----------
923
    model: obj
924
        either a FlowlineModel or a list of model flowlines.
925
    fig
926
    title
927
    """
928

929
    try:
1✔
930
        fls = model.fls
1✔
UNCOV
931
    except AttributeError:
×
UNCOV
932
        fls = model
×
933

934
    n_tribs = len(fls) - 1
1✔
935

936
    axs = []
1✔
937
    if n_tribs == 0:
1✔
UNCOV
938
        if fig is None:
×
UNCOV
939
            fig = plt.figure(figsize=(8, 5))
×
UNCOV
940
        axmaj = fig.add_subplot(111)
×
941
    elif n_tribs <= 3:
1✔
942
        if fig is None:
1✔
UNCOV
943
            fig = plt.figure(figsize=(14, 10))
×
944
        axmaj = plt.subplot2grid((2, 3), (1, 0), colspan=3)
1✔
945
        for i in np.arange(n_tribs):
1✔
946
            axs.append(plt.subplot2grid((2, 3), (0, i)))
1✔
UNCOV
947
    elif n_tribs <= 6:
×
UNCOV
948
        if fig is None:
×
UNCOV
949
            fig = plt.figure(figsize=(14, 10))
×
UNCOV
950
        axmaj = plt.subplot2grid((3, 3), (2, 0), colspan=3)
×
UNCOV
951
        for i in np.arange(n_tribs):
×
UNCOV
952
            j = 0
×
UNCOV
953
            if i >= 3:
×
UNCOV
954
                i -= 3
×
UNCOV
955
                j = 1
×
UNCOV
956
            axs.append(plt.subplot2grid((3, 3), (j, i)))
×
957
    else:
958
        raise NotImplementedError()
959

960
    for i, cls in enumerate(fls):
1✔
961
        if i == n_tribs:
1✔
962
            ax = axmaj
1✔
963
        else:
964
            ax = axs[i]
1✔
965

966
        x = np.arange(cls.nx) * cls.dx * cls.map_dx
1✔
967

968
        # Plot the bed
969
        ax.plot(x, cls.bed_h, color='k', linewidth=2.5, label='Bed (Parab.)')
1✔
970

971
        # Where trapezoid change color
972
        if hasattr(cls, '_do_trapeze') and cls._do_trapeze:
1✔
973
            bed_t = cls.bed_h * np.NaN
1✔
974
            pt = cls.is_trapezoid & (~cls.is_rectangular)
1✔
975
            bed_t[pt] = cls.bed_h[pt]
1✔
976
            ax.plot(x, bed_t, color='rebeccapurple', linewidth=2.5,
1✔
977
                    label='Bed (Trap.)')
978
            bed_t = cls.bed_h * np.NaN
1✔
979
            bed_t[cls.is_rectangular] = cls.bed_h[cls.is_rectangular]
1✔
980
            ax.plot(x, bed_t, color='crimson', linewidth=2.5,
1✔
981
                    label='Bed (Rect.)')
982

983
        # Plot glacier
984
        surfh = surf_to_nan(cls.surface_h, cls.thick)
1✔
985
        ax.plot(x, surfh, color='#003399', linewidth=2, label='Glacier')
1✔
986

987
        # Plot tributaries
988
        for i, l in zip(cls.inflow_indices, cls.inflows):
1✔
989
            if l.thick[-1] > 0:
1✔
990
                ax.plot(x[i], cls.surface_h[i], 's', color='#993399',
1✔
991
                        label='Tributary (active)')
992
            else:
UNCOV
993
                ax.plot(x[i], cls.surface_h[i], 's', color='none',
×
994
                        label='Tributary (inactive)')
995

996
        ax.spines['top'].set_color('none')
1✔
997
        ax.xaxis.set_ticks_position('bottom')
1✔
998
        ax.set_xlabel('Distance along flowline (m)')
1✔
999
        ax.set_ylabel('Altitude (m)')
1✔
1000

1001
    # Title
1002
    plt.title(title, loc='left')
1✔
1003

1004
    # Legend
1005
    handles, labels = ax.get_legend_handles_labels()
1✔
1006
    by_label = OrderedDict(zip(labels, handles))
1✔
1007
    ax.legend(list(by_label.values()), list(by_label.keys()),
1✔
1008
              loc='best', frameon=False)
1009
    fig.tight_layout()
1✔
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