geone 1.3.0__py313-none-manylinux_2_35_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- geone/__init__.py +32 -0
- geone/_version.py +6 -0
- geone/blockdata.py +250 -0
- geone/covModel.py +15529 -0
- geone/customcolors.py +508 -0
- geone/deesse_core/__init__.py +5 -0
- geone/deesse_core/_deesse.so +0 -0
- geone/deesse_core/deesse.py +2450 -0
- geone/deesseinterface.py +11323 -0
- geone/geosclassic_core/__init__.py +5 -0
- geone/geosclassic_core/_geosclassic.so +0 -0
- geone/geosclassic_core/geosclassic.py +1429 -0
- geone/geosclassicinterface.py +20092 -0
- geone/grf.py +5927 -0
- geone/img.py +7152 -0
- geone/imgplot.py +1464 -0
- geone/imgplot3d.py +1918 -0
- geone/markovChain.py +666 -0
- geone/multiGaussian.py +388 -0
- geone/pgs.py +1258 -0
- geone/randProcess.py +1258 -0
- geone/srf.py +3661 -0
- geone/tools.py +861 -0
- geone-1.3.0.dist-info/METADATA +207 -0
- geone-1.3.0.dist-info/RECORD +28 -0
- geone-1.3.0.dist-info/WHEEL +5 -0
- geone-1.3.0.dist-info/licenses/LICENSE +58 -0
- geone-1.3.0.dist-info/top_level.txt +1 -0
geone/imgplot.py
ADDED
|
@@ -0,0 +1,1464 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
# -------------------------------------------------------------------------
|
|
5
|
+
# Python module: 'imgplot.py'
|
|
6
|
+
# author: Julien Straubhaar
|
|
7
|
+
# date: dec-2017
|
|
8
|
+
# -------------------------------------------------------------------------
|
|
9
|
+
|
|
10
|
+
"""
|
|
11
|
+
Module for custom plots of images (class :class:`geone.img.Img`) in 2D.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from geone import img
|
|
15
|
+
from geone.img import Img
|
|
16
|
+
from geone import customcolors as ccol
|
|
17
|
+
from geone.customcolors import add_colorbar
|
|
18
|
+
|
|
19
|
+
import numpy as np
|
|
20
|
+
import matplotlib.pyplot as plt
|
|
21
|
+
|
|
22
|
+
from matplotlib import rcParams as mpl_rcParams
|
|
23
|
+
import matplotlib.colors as mcolors
|
|
24
|
+
|
|
25
|
+
# ============================================================================
|
|
26
|
+
class ImgplotError(Exception):
|
|
27
|
+
"""
|
|
28
|
+
Custom exception related to `imgplot` module.
|
|
29
|
+
"""
|
|
30
|
+
pass
|
|
31
|
+
# ============================================================================
|
|
32
|
+
|
|
33
|
+
# ----------------------------------------------------------------------------
|
|
34
|
+
def drawImage2D(
|
|
35
|
+
im, ix=None, iy=None, iz=None, iv=None,
|
|
36
|
+
plot_empty_grid=False,
|
|
37
|
+
cmap=ccol.cmap_def,
|
|
38
|
+
alpha=None,
|
|
39
|
+
excludedVal=None,
|
|
40
|
+
categ=False, categVal=None,
|
|
41
|
+
categCol=None, categColCycle=False, categColbad=ccol.cbad_def,
|
|
42
|
+
nCategMax=100,
|
|
43
|
+
vmin=None, vmax=None,
|
|
44
|
+
contourf=False,
|
|
45
|
+
contour=False,
|
|
46
|
+
contour_clabel=False,
|
|
47
|
+
levels=None,
|
|
48
|
+
contourf_kwargs=None,
|
|
49
|
+
contour_kwargs={'linestyles':'solid', 'colors':'gray'},
|
|
50
|
+
contour_clabel_kwargs={'inline':1},
|
|
51
|
+
interpolation='none',
|
|
52
|
+
aspect='equal',
|
|
53
|
+
frame=True, xaxis=True, yaxis=True,
|
|
54
|
+
title=None,
|
|
55
|
+
xlabel=None, xticks=None, xticklabels=None, xticklabels_max_decimal=None,
|
|
56
|
+
ylabel=None, yticks=None, yticklabels=None, yticklabels_max_decimal=None,
|
|
57
|
+
clabel=None, cticks=None, cticklabels=None, cticklabels_max_decimal=None,
|
|
58
|
+
colorbar_extend='neither',
|
|
59
|
+
colorbar_aspect=20, colorbar_pad_fraction=1.0,
|
|
60
|
+
showColorbar=True, removeColorbar=False, showColorbarOnly=0,
|
|
61
|
+
verbose=1,
|
|
62
|
+
logger=None,
|
|
63
|
+
**kwargs):
|
|
64
|
+
# animated : bool, default: False
|
|
65
|
+
# keyword argument passed to `matplotlib.pyplot.imshow` for animation...
|
|
66
|
+
"""
|
|
67
|
+
Displays a 2D image (or a slice of a 3D image).
|
|
68
|
+
|
|
69
|
+
Parameters
|
|
70
|
+
----------
|
|
71
|
+
im : :class:`geone.img.Img`
|
|
72
|
+
image
|
|
73
|
+
|
|
74
|
+
ix : int, optional
|
|
75
|
+
grid index along x axis of the yz-slice to be displayed;
|
|
76
|
+
by default (`None`): no slice along x axis
|
|
77
|
+
|
|
78
|
+
iy : int, optional
|
|
79
|
+
grid index along y axis of the xz-slice to be displayed;
|
|
80
|
+
by default (`None`): no slice along y axis
|
|
81
|
+
|
|
82
|
+
iz : int, optional
|
|
83
|
+
grid index along z axis of the xy-slice to be displayed;
|
|
84
|
+
by default (`None`): no slice along z axis, but
|
|
85
|
+
if `ix=None`, `iy=None`, `iz=None`, then `iz=0` is used
|
|
86
|
+
|
|
87
|
+
iv : int, optional
|
|
88
|
+
index of the variable to be displayed;
|
|
89
|
+
by default (`None`): the variable of index 0 (`iv=0` is used) if
|
|
90
|
+
the image has at leas one variable (`im.nv > 0`), otherwise, an "empty"
|
|
91
|
+
grid is displayed (a "fake" variable with `numpy.nan` (missing value)
|
|
92
|
+
over the entire grid is considered)
|
|
93
|
+
|
|
94
|
+
plot_empty_grid : bool, default: False
|
|
95
|
+
if `True`: an "empty" grid is displayed (a "fake" variable with `numpy.nan`
|
|
96
|
+
(missing value) over the entire grid is considered), and `iv` is ignored
|
|
97
|
+
|
|
98
|
+
cmap : colormap
|
|
99
|
+
color map (can be a string, in this case the color map is obtained by
|
|
100
|
+
`matplotlib.pyplot.get_cmap(cmap)`)
|
|
101
|
+
|
|
102
|
+
alpha : float, optional
|
|
103
|
+
value of the "alpha" channel (for transparency);
|
|
104
|
+
by default (`None`): `alpha=1.0` is used (no transparency)
|
|
105
|
+
|
|
106
|
+
excludedVal : sequence of values, or single value, optional
|
|
107
|
+
values to be excluded from the plot;
|
|
108
|
+
note not used if `categ=True` and `categVal` is not `None`
|
|
109
|
+
|
|
110
|
+
categ : bool, default: False
|
|
111
|
+
indicates if the variable of the image to diplay has to be treated as a
|
|
112
|
+
categorical (discrete) variable (`True`), or continuous variable (`False`)
|
|
113
|
+
|
|
114
|
+
categVal : sequence of values, or single value, optional
|
|
115
|
+
used only if `categ=True`:
|
|
116
|
+
explicit list of the category values to be displayed;
|
|
117
|
+
by default (`None`): the list of all unique values are automatically
|
|
118
|
+
retrieved
|
|
119
|
+
|
|
120
|
+
categCol: sequence of colors, optional
|
|
121
|
+
used only if `categ=True`:
|
|
122
|
+
sequence of colors, (given as 3-tuple (RGB code), 4-tuple (RGBA code) or
|
|
123
|
+
str), used for the category values that will be displayed:
|
|
124
|
+
|
|
125
|
+
- if `categVal` is not `None`: `categCol` must have the same length as \
|
|
126
|
+
`categVal`
|
|
127
|
+
- if `categVal=None`:
|
|
128
|
+
- first colors of `categCol` are used if its length is greater than \
|
|
129
|
+
or equal to the number of displayed category values,
|
|
130
|
+
- otherwise: the colors of `categCol` are used cyclically if \
|
|
131
|
+
`categColCycle=True`, and the colors taken from the color map `cmap` \
|
|
132
|
+
are used if `categColCycle=False`
|
|
133
|
+
|
|
134
|
+
categColCycle : bool, default: False
|
|
135
|
+
used only if `categ=True`:
|
|
136
|
+
indicates if the colors of `categCol` can be used cyclically or not
|
|
137
|
+
(when the number of displayed category values exceeds the length of
|
|
138
|
+
`categCol`)
|
|
139
|
+
|
|
140
|
+
categColbad : color
|
|
141
|
+
used only if `categ=True`:
|
|
142
|
+
color (3-tuple (RGB code), 4-tuple (RGBA code) or str) used for bad
|
|
143
|
+
categorical value
|
|
144
|
+
|
|
145
|
+
nCategMax : int, default: 100
|
|
146
|
+
used only if `categ=True`: maximal number of categories, if there is
|
|
147
|
+
more distinct values to be plotted, an error is raised
|
|
148
|
+
|
|
149
|
+
vmin : float, optional
|
|
150
|
+
used only if `categ=False`:
|
|
151
|
+
minimal value to be displayed; by default: minimal value of the displayed
|
|
152
|
+
variable is used for `vmin`
|
|
153
|
+
|
|
154
|
+
vmax : float, optional
|
|
155
|
+
used only if `categ=False`:
|
|
156
|
+
maximal value to be displayed; by default: maximal value of the displayed
|
|
157
|
+
variable is used for `vmax`
|
|
158
|
+
|
|
159
|
+
contourf : bool, default: False
|
|
160
|
+
indicates if `matplotlib.pyplot.contourf` is used, i.e. contour map with
|
|
161
|
+
filled area between levels, instead of standard plot
|
|
162
|
+
(`matplotlib.pyplot.imshow`)
|
|
163
|
+
|
|
164
|
+
contour : bool, default: False
|
|
165
|
+
indicates if contour levels are added to the plot (using
|
|
166
|
+
`matplotlib.pyplot.contour`)
|
|
167
|
+
|
|
168
|
+
contour_clabel : bool, default: False
|
|
169
|
+
indicates if labels are added to contour (if `contour=True`)
|
|
170
|
+
|
|
171
|
+
levels : array-like, or int, optional
|
|
172
|
+
keyword argument 'levels' passed to `matplotlib.pyplot.contourf` (if
|
|
173
|
+
`contourf=True`) and/or `matplotlib.pyplot.contour` (if `contour=True`)
|
|
174
|
+
|
|
175
|
+
contourf_kwargs : dict, optional
|
|
176
|
+
keyword arguments passed to `matplotlib.pyplot.contourf` (if
|
|
177
|
+
`contourf=True`); note: the parameters `levels` (see above) is used as
|
|
178
|
+
keyword argument, i.e. it prevails over the key 'levels' in
|
|
179
|
+
`contourf_kwargs` (if given)
|
|
180
|
+
|
|
181
|
+
contour_kwargs : dict
|
|
182
|
+
keyword arguments passed to `matplotlib.pyplot.contour` (if
|
|
183
|
+
`contour=True`); note: the parameters `levels` (see above) is used as
|
|
184
|
+
keyword argument, i.e. it prevails over the key 'levels' in
|
|
185
|
+
`contour_kwargs` (if given)
|
|
186
|
+
|
|
187
|
+
contour_clabel_kwargs : dict
|
|
188
|
+
keyword arguments passed to `matplotlib.pyplot.clabel` (if
|
|
189
|
+
`contour_clabel=True`)
|
|
190
|
+
|
|
191
|
+
interpolation : str, default: 'none'
|
|
192
|
+
keyword argument 'interpolation' to be passed `matplotlib.pyplot.imshow`
|
|
193
|
+
|
|
194
|
+
aspect : str, or scalar, default: 'equal'
|
|
195
|
+
keyword argument 'aspect' to be passed `matplotlib.pyplot.imshow`
|
|
196
|
+
|
|
197
|
+
frame : bool, default: True
|
|
198
|
+
indicates if a frame is drawn around the image
|
|
199
|
+
|
|
200
|
+
xaxis : bool, default: True
|
|
201
|
+
indicates if x axis is visible
|
|
202
|
+
|
|
203
|
+
yaxis : bool, default: True
|
|
204
|
+
indicates if y axis is visible
|
|
205
|
+
|
|
206
|
+
title : str, optional
|
|
207
|
+
title of the figure
|
|
208
|
+
|
|
209
|
+
xlabel : str, optional
|
|
210
|
+
label for x axis
|
|
211
|
+
|
|
212
|
+
ylabel : str, optional
|
|
213
|
+
label for y axis
|
|
214
|
+
|
|
215
|
+
clabel : str, optional
|
|
216
|
+
label for color bar
|
|
217
|
+
|
|
218
|
+
xticks : sequence of values, optional
|
|
219
|
+
values where to place ticks along x axis
|
|
220
|
+
|
|
221
|
+
yticks : sequence of values, optional
|
|
222
|
+
values where to place ticks along y axis
|
|
223
|
+
|
|
224
|
+
cticks : sequence of values, optional
|
|
225
|
+
values where to place ticks along the color bar
|
|
226
|
+
|
|
227
|
+
xticklabels : sequence of strs, optional,
|
|
228
|
+
sequence of strings for ticks along x axis
|
|
229
|
+
|
|
230
|
+
yticklabels : sequence of strs, optional,
|
|
231
|
+
sequence of strings for ticks along y axis
|
|
232
|
+
|
|
233
|
+
cticklabels : sequence of strs, optional,
|
|
234
|
+
sequence of strings for ticks along the color bar
|
|
235
|
+
|
|
236
|
+
xticklabels_max_decimal : int, optional
|
|
237
|
+
maximal number of decimals (fractional part) for tick labels along x axis
|
|
238
|
+
|
|
239
|
+
yticklabels_max_decimal : int, optional
|
|
240
|
+
maximal number of decimals (fractional part) for tick labels along y axis
|
|
241
|
+
|
|
242
|
+
cticklabels_max_decimal : int, optional
|
|
243
|
+
maximal number of decimals (fractional part) for tick labels along the
|
|
244
|
+
color bar
|
|
245
|
+
|
|
246
|
+
colorbar_extend : str {'neither', 'both', 'min', 'max'}, default: 'neither'
|
|
247
|
+
used only if `categ=False`:
|
|
248
|
+
keyword argument 'extend' to be passed to `matplotlib.pyplot.colorbar`
|
|
249
|
+
(or `geone.customcolors.add_colorbar`)
|
|
250
|
+
|
|
251
|
+
colorbar_aspect : float or int, default: 20
|
|
252
|
+
keyword argument 'aspect' to be passed to
|
|
253
|
+
`geone.customcolors.add_colorbar`
|
|
254
|
+
|
|
255
|
+
colorbar_pad_fraction : float or int, default: 1.0
|
|
256
|
+
keyword argument 'pad_fraction' to be passed to
|
|
257
|
+
`geone.customcolors.add_colorbar`
|
|
258
|
+
|
|
259
|
+
showColorbar : bool, default: True
|
|
260
|
+
indicates if the color bar (vertical) is shown
|
|
261
|
+
|
|
262
|
+
removeColorbar : bool, default: False
|
|
263
|
+
if True (and if `showColorbar=True`), then the colorbar is removed;
|
|
264
|
+
note: it can be useful to draw and then remove the color bar for size of
|
|
265
|
+
the plotted image...)
|
|
266
|
+
|
|
267
|
+
showColorbarOnly : int, default: 0
|
|
268
|
+
mode defining how the color bar (vertical) is shown:
|
|
269
|
+
|
|
270
|
+
- `showColorbarOnly=0`: not used / not applied
|
|
271
|
+
- `showColorbarOnly>0`: only the color bar is shown (even if \
|
|
272
|
+
`showColorbar=False` or if `removeColorbar=True`):
|
|
273
|
+
- `showColorbarOnly=1`: the plotted image is "cleared"
|
|
274
|
+
- `showColorbarOnly=2`: an image of same color as the background is \
|
|
275
|
+
drawn onto the plotted image
|
|
276
|
+
|
|
277
|
+
verbose : int, default: 1
|
|
278
|
+
verbose mode, higher implies more printing (info)
|
|
279
|
+
|
|
280
|
+
logger : :class:`logging.Logger`, optional
|
|
281
|
+
logger (see package `logging`)
|
|
282
|
+
if specified, messages are written via `logger` (no print)
|
|
283
|
+
|
|
284
|
+
kwargs : dict
|
|
285
|
+
additional keyword arguments : each keyword argument with the key
|
|
286
|
+
'xxx_<name>' will be passed as keyword argument with the key '<name>' to
|
|
287
|
+
a function related to 'xxx';
|
|
288
|
+
possibilities for 'xxx\\_' and related function:
|
|
289
|
+
|
|
290
|
+
.. list-table::
|
|
291
|
+
:widths: 25 45
|
|
292
|
+
:header-rows: 1
|
|
293
|
+
|
|
294
|
+
* - string (prefix)
|
|
295
|
+
- method from `Axes` from `matplotlib`
|
|
296
|
+
* - 'title\\_'
|
|
297
|
+
- `ax.set_title()`
|
|
298
|
+
* - 'xlabel\\_'
|
|
299
|
+
- `ax.set_xlabel()`
|
|
300
|
+
* - 'xticks\\_'
|
|
301
|
+
- `ax.set_xticks()`
|
|
302
|
+
* - 'xticklabels\\_'
|
|
303
|
+
- `ax.set_xticklabels()`
|
|
304
|
+
* - 'ylabel\\_'
|
|
305
|
+
- `ax.set_ylabel()`
|
|
306
|
+
* - 'yticks\\_'
|
|
307
|
+
- `ax.set_yticks()`
|
|
308
|
+
* - 'yticklabels\\_'
|
|
309
|
+
- `ax.set_yticklabels()`
|
|
310
|
+
* - 'clabel\\_'
|
|
311
|
+
- `cbar.set_label()`
|
|
312
|
+
* - 'cticks\\_'
|
|
313
|
+
- `cbar.set_ticks()`
|
|
314
|
+
* - 'cticklabels\\_'
|
|
315
|
+
- `cbar.ax.set_yticklabels()`
|
|
316
|
+
|
|
317
|
+
for examples:
|
|
318
|
+
|
|
319
|
+
- 'title_fontsize', '<x|y|c>label_fontsize' (keys)
|
|
320
|
+
- 'title_fontweight', '<x|y|c>label_fontweight' (keys), with \
|
|
321
|
+
possible values: numeric value in 0-1000 or \
|
|
322
|
+
'ultralight', 'light', 'normal', 'regular', 'book', 'medium', \
|
|
323
|
+
'roman', 'semibold', 'demibold', 'demi', 'bold', 'heavy', \
|
|
324
|
+
'extra bold', 'black'
|
|
325
|
+
|
|
326
|
+
Note that
|
|
327
|
+
|
|
328
|
+
- default value for font size is `matplotlib.rcParams['font.size']`
|
|
329
|
+
- default value for font weight is `matplotlib.rcParams['font.weight']`
|
|
330
|
+
|
|
331
|
+
Returns
|
|
332
|
+
-------
|
|
333
|
+
imout : list of `matplotlib` object
|
|
334
|
+
list of plotted objects
|
|
335
|
+
"""
|
|
336
|
+
# - 'title\_' fun: ax.set_title()
|
|
337
|
+
# - 'xlabel\_' fun: ax.set_xlabel()
|
|
338
|
+
# - 'xticks\_' fun: ax.set_xticks()
|
|
339
|
+
# - 'xticklabels\_' fun: ax.set_xticklabels()
|
|
340
|
+
# - 'ylabel\_' fun: ax.set_ylabel()
|
|
341
|
+
# - 'yticks\_' fun: ax.set_yticks()
|
|
342
|
+
# - 'yticklabels\_' fun: ax.set_yticklabels()
|
|
343
|
+
# - 'clabel\_' fun: cbar.set_label()
|
|
344
|
+
# - 'cticks\_' fun: cbar.set_ticks()
|
|
345
|
+
# - 'cticklabels\_' fun: cbar.ax.set_yticklabels()
|
|
346
|
+
fname = 'drawImage2D'
|
|
347
|
+
|
|
348
|
+
# Initialization for output
|
|
349
|
+
imout = []
|
|
350
|
+
#ax, cbar = None, None
|
|
351
|
+
|
|
352
|
+
if plot_empty_grid:
|
|
353
|
+
iv = None
|
|
354
|
+
else:
|
|
355
|
+
# Check / set iv
|
|
356
|
+
if iv is None:
|
|
357
|
+
if im.nv > 0:
|
|
358
|
+
iv = 0
|
|
359
|
+
else:
|
|
360
|
+
if iv < 0:
|
|
361
|
+
iv = im.nv + iv
|
|
362
|
+
|
|
363
|
+
if iv < 0 or iv >= im.nv:
|
|
364
|
+
err_msg = f'{fname}: invalid `iv` index'
|
|
365
|
+
if logger: logger.error(err_msg)
|
|
366
|
+
raise ImgplotError(err_msg)
|
|
367
|
+
|
|
368
|
+
# Check slice direction and indices
|
|
369
|
+
n = int(ix is not None) + int(iy is not None) + int(iz is not None)
|
|
370
|
+
|
|
371
|
+
if n == 0:
|
|
372
|
+
sliceDir = 'z'
|
|
373
|
+
iz = 0
|
|
374
|
+
|
|
375
|
+
elif n==1:
|
|
376
|
+
if ix is not None:
|
|
377
|
+
if ix < 0:
|
|
378
|
+
ix = im.nx + ix
|
|
379
|
+
|
|
380
|
+
if ix < 0 or ix >= im.nx:
|
|
381
|
+
err_msg = f'{fname}: invalid `ix` index'
|
|
382
|
+
if logger: logger.error(err_msg)
|
|
383
|
+
raise ImgplotError(err_msg)
|
|
384
|
+
|
|
385
|
+
sliceDir = 'x'
|
|
386
|
+
|
|
387
|
+
elif iy is not None:
|
|
388
|
+
if iy < 0:
|
|
389
|
+
iy = im.ny + iy
|
|
390
|
+
|
|
391
|
+
if iy < 0 or iy >= im.ny:
|
|
392
|
+
err_msg = f'{fname}: invalid `iy` index'
|
|
393
|
+
if logger: logger.error(err_msg)
|
|
394
|
+
raise ImgplotError(err_msg)
|
|
395
|
+
|
|
396
|
+
sliceDir = 'y'
|
|
397
|
+
|
|
398
|
+
else: # iz is not None
|
|
399
|
+
if iz < 0:
|
|
400
|
+
iz = im.nz + iz
|
|
401
|
+
|
|
402
|
+
if iz < 0 or iz >= im.nz:
|
|
403
|
+
err_msg = f'{fname}: invalid `iz` index'
|
|
404
|
+
if logger: logger.error(err_msg)
|
|
405
|
+
raise ImgplotError(err_msg)
|
|
406
|
+
|
|
407
|
+
sliceDir = 'z'
|
|
408
|
+
|
|
409
|
+
else: # n > 1
|
|
410
|
+
err_msg = f'{fname}: slice specified in more than one direction'
|
|
411
|
+
if logger: logger.error(err_msg)
|
|
412
|
+
raise ImgplotError(err_msg)
|
|
413
|
+
|
|
414
|
+
# Extract what to be plotted
|
|
415
|
+
if sliceDir == 'x':
|
|
416
|
+
dim0 = im.ny
|
|
417
|
+
min0 = im.oy
|
|
418
|
+
max0 = im.ymax()
|
|
419
|
+
|
|
420
|
+
dim1 = im.nz
|
|
421
|
+
min1 = im.oz
|
|
422
|
+
max1 = im.zmax()
|
|
423
|
+
if iv is None: # empty image
|
|
424
|
+
zz = np.nan * np.ones((im.nz, im.ny))
|
|
425
|
+
else:
|
|
426
|
+
zz = np.array(im.val[iv, :, :, ix].reshape(dim1, dim0)) # np.array() to get a copy
|
|
427
|
+
# reshape to force 2-dimensional array
|
|
428
|
+
|
|
429
|
+
elif sliceDir == 'y':
|
|
430
|
+
dim0 = im.nx
|
|
431
|
+
min0 = im.ox
|
|
432
|
+
max0 = im.xmax()
|
|
433
|
+
|
|
434
|
+
dim1 = im.nz
|
|
435
|
+
min1 = im.oz
|
|
436
|
+
max1 = im.zmax()
|
|
437
|
+
if iv is None: # empty image
|
|
438
|
+
zz = np.nan * np.ones((im.nz, im.nx))
|
|
439
|
+
else:
|
|
440
|
+
zz = np.array(im.val[iv, :, iy, :].reshape(dim1, dim0)) # np.array() to get a copy
|
|
441
|
+
|
|
442
|
+
else: # sliceDir == 'z'
|
|
443
|
+
dim0 = im.nx
|
|
444
|
+
min0 = im.ox
|
|
445
|
+
max0 = im.xmax()
|
|
446
|
+
|
|
447
|
+
dim1 = im.ny
|
|
448
|
+
min1 = im.oy
|
|
449
|
+
max1 = im.ymax()
|
|
450
|
+
if iv is None: # empty image
|
|
451
|
+
zz = np.nan * np.ones((im.ny, im.nx))
|
|
452
|
+
else:
|
|
453
|
+
zz = np.array(im.val[iv, iz, :, :].reshape(dim1, dim0)) # np.array() to get a copy
|
|
454
|
+
|
|
455
|
+
# Get the color map
|
|
456
|
+
if isinstance(cmap, str):
|
|
457
|
+
try:
|
|
458
|
+
cmap = plt.get_cmap(cmap)
|
|
459
|
+
except:
|
|
460
|
+
err_msg = f'{fname}: invalid `cmap` string'
|
|
461
|
+
if logger: logger.error(err_msg)
|
|
462
|
+
raise ImgplotError(err_msg)
|
|
463
|
+
|
|
464
|
+
if categ:
|
|
465
|
+
# --- Treat categorical variable ---
|
|
466
|
+
if categCol is not None \
|
|
467
|
+
and type(categCol) is not list \
|
|
468
|
+
and type(categCol) is not tuple:
|
|
469
|
+
err_msg = f'{fname}: `categCol` must be a list or a tuple (if not `None`)'
|
|
470
|
+
if logger: logger.error(err_msg)
|
|
471
|
+
raise ImgplotError(err_msg)
|
|
472
|
+
|
|
473
|
+
# Get array 'dval' of displayed values
|
|
474
|
+
if categVal is not None:
|
|
475
|
+
dval = np.array(categVal).reshape(-1) # force to be a 1d array
|
|
476
|
+
|
|
477
|
+
if len(np.unique(dval)) != len(dval):
|
|
478
|
+
err_msg = f'{fname}: `categVal` contains duplicated entries'
|
|
479
|
+
if logger: logger.error(err_msg)
|
|
480
|
+
raise ImgplotError(err_msg)
|
|
481
|
+
|
|
482
|
+
# Check 'categCol' (if not None)
|
|
483
|
+
if categCol is not None and len(categCol) != len(dval):
|
|
484
|
+
err_msg = f'{fname}: length of `categVal` and length of `categCol` differ'
|
|
485
|
+
if logger: logger.error(err_msg)
|
|
486
|
+
raise ImgplotError(err_msg)
|
|
487
|
+
|
|
488
|
+
else:
|
|
489
|
+
# Possibly exclude values from zz
|
|
490
|
+
if excludedVal is not None:
|
|
491
|
+
for val in np.array(excludedVal).reshape(-1):
|
|
492
|
+
np.putmask(zz, zz == val, np.nan)
|
|
493
|
+
|
|
494
|
+
# Get the unique value in zz
|
|
495
|
+
dval = np.array([v for v in np.unique(zz).reshape(-1) if ~np.isnan(v)])
|
|
496
|
+
|
|
497
|
+
if not len(dval) and verbose > 0: # len(dval) == 0
|
|
498
|
+
if logger:
|
|
499
|
+
logger.warning(f'{fname}: no value to be drawn!')
|
|
500
|
+
else:
|
|
501
|
+
print(f'{fname}: WARNING: no value to be drawn!')
|
|
502
|
+
|
|
503
|
+
if len(dval) > nCategMax:
|
|
504
|
+
# Prevent from plotting in category mode if too many categories...
|
|
505
|
+
err_msg = f'{fname}: too many categories to be plotted (> `nCategMax` = {nCategMax})'
|
|
506
|
+
if logger: logger.error(err_msg)
|
|
507
|
+
raise ImgplotError(err_msg)
|
|
508
|
+
|
|
509
|
+
# Replace dval[i] by i in zz and other values by np.nan
|
|
510
|
+
zz2 = np.array(zz) # copy array
|
|
511
|
+
zz[...] = np.nan # initialize
|
|
512
|
+
for i, v in enumerate(dval):
|
|
513
|
+
zz[zz2 == v] = i
|
|
514
|
+
|
|
515
|
+
del zz2
|
|
516
|
+
|
|
517
|
+
# Set 'colorList': the list of colors to use
|
|
518
|
+
colorList = None
|
|
519
|
+
if categCol is not None:
|
|
520
|
+
if len(categCol) >= len(dval):
|
|
521
|
+
colorList = [categCol[i] for i in range(len(dval))]
|
|
522
|
+
# colorList = [mcolors.ColorConverter().to_rgba(categCol[i]) for i in range(len(dval))]
|
|
523
|
+
|
|
524
|
+
elif categColCycle:
|
|
525
|
+
if verbose > 0:
|
|
526
|
+
if logger:
|
|
527
|
+
logger.warning(f'{fname}: `categCol` is used cyclically (too few entries)')
|
|
528
|
+
else:
|
|
529
|
+
print(f'{fname}: WARNING: `categCol` is used cyclically (too few entries)')
|
|
530
|
+
colorList = [categCol[i%len(categCol)] for i in range(len(dval))]
|
|
531
|
+
|
|
532
|
+
else:
|
|
533
|
+
if verbose > 0:
|
|
534
|
+
if logger:
|
|
535
|
+
logger.warning(f'{fname}: `categCol` not used (too few entries)')
|
|
536
|
+
else:
|
|
537
|
+
print(f'{fname}: WARNING: `categCol` not used (too few entries)')
|
|
538
|
+
|
|
539
|
+
if colorList is None:
|
|
540
|
+
# Use colors from cmap
|
|
541
|
+
colorList = [cmap(x) for x in np.arange(len(dval)) * 1.0/(len(dval)-1)]
|
|
542
|
+
|
|
543
|
+
# Set the colormap: 'cmap'
|
|
544
|
+
if len(dval) == 1:
|
|
545
|
+
cmap = ccol.custom_cmap([colorList[0], colorList[0]], ncol=2,
|
|
546
|
+
cbad=categColbad, alpha=alpha)
|
|
547
|
+
|
|
548
|
+
else: # len(dval) == len(colorList) > 1
|
|
549
|
+
# cmap = mcolors.ListedColormap(colorList)
|
|
550
|
+
cmap = ccol.custom_cmap(colorList, ncol=len(colorList),
|
|
551
|
+
cbad=categColbad, alpha=alpha)
|
|
552
|
+
|
|
553
|
+
# Set the min and max of the colorbar
|
|
554
|
+
vmin, vmax = -0.5, len(dval) - 0.5
|
|
555
|
+
|
|
556
|
+
# Set colorbar ticks and ticklabels if not given
|
|
557
|
+
if cticks is None:
|
|
558
|
+
cticks = range(len(dval))
|
|
559
|
+
|
|
560
|
+
if cticklabels is None:
|
|
561
|
+
#cticklabels = [f'{v:.3g}' for v in cticks]
|
|
562
|
+
cticklabels = [f'{v:.3g}' for v in dval]
|
|
563
|
+
|
|
564
|
+
# Reset cextend if needed
|
|
565
|
+
colorbar_extend = 'neither'
|
|
566
|
+
|
|
567
|
+
else: # categ == False
|
|
568
|
+
# --- Treat continuous variable ---
|
|
569
|
+
# Possibly exclude values from zz
|
|
570
|
+
if excludedVal is not None:
|
|
571
|
+
for val in np.array(excludedVal).reshape(-1): # force to be a 1d array
|
|
572
|
+
np.putmask(zz, zz == val, np.nan)
|
|
573
|
+
|
|
574
|
+
# Generate "sub-dictionaries" from kwargs
|
|
575
|
+
# For each item 'xxx_<name>':<value> from kwargs (whose the key begins
|
|
576
|
+
# with "xxx_"): the item '<name>':<value> is added to the dictionary
|
|
577
|
+
# xxx_kwargs
|
|
578
|
+
# "xxx_" is replaced by the strings below (in sub_prefix list)
|
|
579
|
+
# These sub-dictionaries are passed (unpacked) as keyword arguments to
|
|
580
|
+
# functions that draw labels, ticklabels, ...
|
|
581
|
+
title_kwargs = {}
|
|
582
|
+
|
|
583
|
+
xlabel_kwargs = {}
|
|
584
|
+
xticks_kwargs = {}
|
|
585
|
+
xticklabels_kwargs = {}
|
|
586
|
+
|
|
587
|
+
ylabel_kwargs = {}
|
|
588
|
+
yticks_kwargs = {}
|
|
589
|
+
yticklabels_kwargs = {}
|
|
590
|
+
|
|
591
|
+
clabel_kwargs = {}
|
|
592
|
+
cticks_kwargs = {}
|
|
593
|
+
cticklabels_kwargs = {}
|
|
594
|
+
|
|
595
|
+
colorbar_kwargs = {'aspect':colorbar_aspect, 'pad_fraction':colorbar_pad_fraction}
|
|
596
|
+
|
|
597
|
+
sub_prefix = ['title_',
|
|
598
|
+
'xlabel_', 'xticks_', 'xticklabels_',
|
|
599
|
+
'ylabel_', 'yticks_', 'yticklabels_',
|
|
600
|
+
'clabel_', 'cticks_', 'cticklabels_',
|
|
601
|
+
'colorbar_']
|
|
602
|
+
sub_kwargs = [title_kwargs,
|
|
603
|
+
xlabel_kwargs, xticks_kwargs, xticklabels_kwargs,
|
|
604
|
+
ylabel_kwargs, yticks_kwargs, yticklabels_kwargs,
|
|
605
|
+
clabel_kwargs, cticks_kwargs, cticklabels_kwargs,
|
|
606
|
+
colorbar_kwargs] # list of dictionaries
|
|
607
|
+
|
|
608
|
+
for k, v in kwargs.items():
|
|
609
|
+
for i in range(len(sub_kwargs)):
|
|
610
|
+
n = len(sub_prefix[i])
|
|
611
|
+
if k[0:n] == sub_prefix[i]:
|
|
612
|
+
sub_kwargs[i][k[n:]] = v # add item k[n:]:v to dictionary sub_kwargs[i]
|
|
613
|
+
|
|
614
|
+
if showColorbarOnly:
|
|
615
|
+
# Overwrite some parameters if needed
|
|
616
|
+
frame = False
|
|
617
|
+
xaxis = False
|
|
618
|
+
yaxis = False
|
|
619
|
+
title = None
|
|
620
|
+
showColorbar = True
|
|
621
|
+
removeColorbar = False
|
|
622
|
+
|
|
623
|
+
# Get current axis (for plotting)
|
|
624
|
+
ax = plt.gca()
|
|
625
|
+
|
|
626
|
+
# image plot
|
|
627
|
+
im_plot = ax.imshow(zz, cmap=cmap, alpha=alpha, vmin=vmin, vmax=vmax,
|
|
628
|
+
origin='lower', extent=[min0, max0, min1, max1],
|
|
629
|
+
interpolation=interpolation, aspect=aspect) #, animated=animated)
|
|
630
|
+
imout.append(im_plot)
|
|
631
|
+
|
|
632
|
+
if contourf:
|
|
633
|
+
# imshow is still used above to account for 'aspect'
|
|
634
|
+
# Set key word argument 'levels' from the argument 'levels'
|
|
635
|
+
if contourf_kwargs is None:
|
|
636
|
+
contourf_kwargs = {}
|
|
637
|
+
contourf_kwargs['levels'] = levels
|
|
638
|
+
im_contf = ax.contourf(zz, cmap=cmap, alpha=alpha, vmin=vmin, vmax=vmax,
|
|
639
|
+
origin='lower', extent=[min0, max0, min1, max1],
|
|
640
|
+
**contourf_kwargs)
|
|
641
|
+
imout.append(im_contf)
|
|
642
|
+
|
|
643
|
+
if contour:
|
|
644
|
+
# Set key word argument 'levels' from the argument 'levels'
|
|
645
|
+
contour_kwargs['levels']=levels
|
|
646
|
+
im_cont = ax.contour(zz,
|
|
647
|
+
origin='lower', extent=[min0, max0, min1, max1],
|
|
648
|
+
**contour_kwargs)
|
|
649
|
+
imout.append(im_cont)
|
|
650
|
+
if contour_clabel:
|
|
651
|
+
ax.clabel(im_cont, **contour_clabel_kwargs)
|
|
652
|
+
|
|
653
|
+
# title
|
|
654
|
+
if title is not None:
|
|
655
|
+
ax.set_title(title, **title_kwargs)
|
|
656
|
+
|
|
657
|
+
# xlabel, xticks and xticklabels
|
|
658
|
+
if xlabel is not None:
|
|
659
|
+
ax.set_xlabel(xlabel, **xlabel_kwargs)
|
|
660
|
+
|
|
661
|
+
if xticks is not None:
|
|
662
|
+
ax.set_xticks(xticks, **xticks_kwargs)
|
|
663
|
+
|
|
664
|
+
if xticklabels is not None:
|
|
665
|
+
ax.set_xticklabels(xticklabels, **xticklabels_kwargs)
|
|
666
|
+
elif xticklabels_max_decimal is not None:
|
|
667
|
+
s = 10**xticklabels_max_decimal
|
|
668
|
+
labels = [np.round(t*s)/s for t in ax.get_xticks()]
|
|
669
|
+
ax.set_xticklabels(labels, **xticklabels_kwargs)
|
|
670
|
+
elif len(xticklabels_kwargs):
|
|
671
|
+
ax.set_xticklabels(ax.get_xticks(), **xticklabels_kwargs)
|
|
672
|
+
# else... default xticklabels....
|
|
673
|
+
|
|
674
|
+
# ylabel, yticks and yticklabels
|
|
675
|
+
if ylabel is not None:
|
|
676
|
+
ax.set_ylabel(ylabel, **ylabel_kwargs)
|
|
677
|
+
|
|
678
|
+
if yticks is not None:
|
|
679
|
+
ax.set_yticks(yticks, **yticks_kwargs)
|
|
680
|
+
|
|
681
|
+
if yticklabels is not None:
|
|
682
|
+
ax.set_yticklabels(yticklabels, **yticklabels_kwargs)
|
|
683
|
+
elif yticklabels_max_decimal is not None:
|
|
684
|
+
s = 10**yticklabels_max_decimal
|
|
685
|
+
labels = [np.round(t*s)/s for t in ax.get_yticks()]
|
|
686
|
+
ax.set_yticklabels(labels, **yticklabels_kwargs)
|
|
687
|
+
elif len(yticklabels_kwargs):
|
|
688
|
+
ax.set_yticklabels(ax.get_yticks(), **yticklabels_kwargs)
|
|
689
|
+
# else... default yticklabels....
|
|
690
|
+
|
|
691
|
+
# Display or hide: frame, xaxis, yaxis
|
|
692
|
+
if not frame:
|
|
693
|
+
ax.set_frame_on(False)
|
|
694
|
+
|
|
695
|
+
if not xaxis:
|
|
696
|
+
ax.get_xaxis().set_visible(False)
|
|
697
|
+
|
|
698
|
+
if not yaxis:
|
|
699
|
+
ax.get_yaxis().set_visible(False)
|
|
700
|
+
|
|
701
|
+
# Colorbar
|
|
702
|
+
if showColorbar:
|
|
703
|
+
#cbar_kwargs = {'aspect':colorbar_aspect, 'pad_fraction':colorbar_pad_fraction}
|
|
704
|
+
if not contourf:
|
|
705
|
+
colorbar_kwargs['extend']=colorbar_extend
|
|
706
|
+
cbar = add_colorbar(im_plot, **colorbar_kwargs)
|
|
707
|
+
|
|
708
|
+
if clabel is not None:
|
|
709
|
+
cbar.set_label(clabel, **clabel_kwargs)
|
|
710
|
+
|
|
711
|
+
if cticks is not None:
|
|
712
|
+
cbar.set_ticks(cticks, **cticks_kwargs)
|
|
713
|
+
|
|
714
|
+
if cticklabels is not None:
|
|
715
|
+
cbar.ax.set_yticklabels(cticklabels, **cticklabels_kwargs)
|
|
716
|
+
elif cticklabels_max_decimal is not None:
|
|
717
|
+
s = 10**cticklabels_max_decimal
|
|
718
|
+
labels = [np.round(t*s)/s for t in cbar.get_ticks()]
|
|
719
|
+
cbar.ax.set_yticklabels(labels, **cticklabels_kwargs)
|
|
720
|
+
elif len(cticklabels_kwargs):
|
|
721
|
+
cbar.ax.set_yticklabels(cbar.get_ticks(), **cticklabels_kwargs)
|
|
722
|
+
# else... default cticklabels....
|
|
723
|
+
|
|
724
|
+
if removeColorbar:
|
|
725
|
+
# cbar.ax.get_xaxis().set_visible(False)
|
|
726
|
+
# cbar.ax.get_yaxis().set_visible(False)
|
|
727
|
+
# cbar.ax.clear()
|
|
728
|
+
cbar.ax.set_visible(False)
|
|
729
|
+
|
|
730
|
+
if showColorbarOnly:
|
|
731
|
+
if showColorbarOnly == 1:
|
|
732
|
+
ax.clear() # change the size of the colorbar...
|
|
733
|
+
else:
|
|
734
|
+
# Trick: redraw the image in background color...
|
|
735
|
+
zz[...] = 0
|
|
736
|
+
# bg_color = mpl_rcParams['figure.facecolor'] # background color
|
|
737
|
+
bg_color = ax.get_facecolor() # background color
|
|
738
|
+
# bg_color = plt.gcf().get_facecolor() # background color
|
|
739
|
+
ncmap = ccol.custom_cmap([bg_color,bg_color], ncol=2)
|
|
740
|
+
ax.imshow(zz, cmap=ncmap)
|
|
741
|
+
ax.set_frame_on(True)
|
|
742
|
+
for pos in ('bottom', 'top', 'right', 'left'):
|
|
743
|
+
ax.spines[pos].set_color(bg_color)
|
|
744
|
+
ax.spines[pos].set_linewidth(10)
|
|
745
|
+
|
|
746
|
+
return imout
|
|
747
|
+
#return (ax, cbar)
|
|
748
|
+
# ----------------------------------------------------------------------------
|
|
749
|
+
|
|
750
|
+
# ----------------------------------------------------------------------------
|
|
751
|
+
def get_colors_from_values(
|
|
752
|
+
val,
|
|
753
|
+
cmap=ccol.cmap_def,
|
|
754
|
+
alpha=None,
|
|
755
|
+
excludedVal=None,
|
|
756
|
+
categ=False, categVal=None,
|
|
757
|
+
categCol=None, categColCycle=False, categColbad=ccol.cbad_def,
|
|
758
|
+
vmin=None, vmax=None,
|
|
759
|
+
cmin=None, cmax=None,
|
|
760
|
+
verbose=1,
|
|
761
|
+
logger=None):
|
|
762
|
+
"""
|
|
763
|
+
Gets the colors for given values, according to color settings as used in function :func:`drawImage2D`.
|
|
764
|
+
|
|
765
|
+
Parameters
|
|
766
|
+
----------
|
|
767
|
+
val : array-like of floats, or float
|
|
768
|
+
values for which the colors have to be retrieved
|
|
769
|
+
|
|
770
|
+
cmap : see function :func:`drawImage2D`
|
|
771
|
+
|
|
772
|
+
alpha : see function :func:`drawImage2D`
|
|
773
|
+
|
|
774
|
+
excludedVal : see function :func:`drawImage2D`
|
|
775
|
+
|
|
776
|
+
categ : see function :func:`drawImage2D`
|
|
777
|
+
|
|
778
|
+
categVal : see function :func:`drawImage2D`
|
|
779
|
+
|
|
780
|
+
categCol : see function :func:`drawImage2D`
|
|
781
|
+
|
|
782
|
+
categColCycle : see function :func:`drawImage2D`
|
|
783
|
+
|
|
784
|
+
categColbad : see function :func:`drawImage2D`
|
|
785
|
+
|
|
786
|
+
vmin : see function :func:`drawImage2D`
|
|
787
|
+
|
|
788
|
+
vmax : see function :func:`drawImage2D`
|
|
789
|
+
|
|
790
|
+
cmin : float, optional
|
|
791
|
+
alternative keyword for `vmin` (for compatibility with color settings
|
|
792
|
+
in the functions of the module `geone.imgplot3d`)
|
|
793
|
+
|
|
794
|
+
cmax : float, optional
|
|
795
|
+
alternative keyword for `vmax` (for compatibility with color settings
|
|
796
|
+
in the functions of the module `geone.imgplot3d`)
|
|
797
|
+
|
|
798
|
+
verbose : int, default: 1
|
|
799
|
+
verbose mode, higher implies more printing (info)
|
|
800
|
+
|
|
801
|
+
logger : :class:`logging.Logger`, optional
|
|
802
|
+
logger (see package `logging`)
|
|
803
|
+
if specified, messages are written via `logger` (no print)
|
|
804
|
+
|
|
805
|
+
Returns
|
|
806
|
+
-------
|
|
807
|
+
col : 1D array of colors
|
|
808
|
+
colors used for values in `val` according to the given settings
|
|
809
|
+
"""
|
|
810
|
+
fname = 'get_colors_from_values'
|
|
811
|
+
|
|
812
|
+
# Check vmin, cmin and vmax, cmax
|
|
813
|
+
if vmin is not None and cmin is not None:
|
|
814
|
+
err_msg = f'{fname}: use `vmin` or `cmin` (not both)'
|
|
815
|
+
if logger: logger.error(err_msg)
|
|
816
|
+
raise ImgplotError(err_msg)
|
|
817
|
+
|
|
818
|
+
if vmax is not None and cmax is not None:
|
|
819
|
+
err_msg = f'{fname}: use `vmax` or `cmax` (not both)'
|
|
820
|
+
if logger: logger.error(err_msg)
|
|
821
|
+
raise ImgplotError(err_msg)
|
|
822
|
+
|
|
823
|
+
if vmin is None:
|
|
824
|
+
vmin = cmin
|
|
825
|
+
|
|
826
|
+
if vmax is None:
|
|
827
|
+
vmax = cmax
|
|
828
|
+
|
|
829
|
+
# Copy val in a 1d array
|
|
830
|
+
zz = np.copy(np.atleast_1d(val)).reshape(-1)
|
|
831
|
+
|
|
832
|
+
# --- Code adapted from function drawImage2D - start ----
|
|
833
|
+
# Get the color map
|
|
834
|
+
if isinstance(cmap, str):
|
|
835
|
+
try:
|
|
836
|
+
cmap = plt.get_cmap(cmap)
|
|
837
|
+
except:
|
|
838
|
+
err_msg = f'{fname}: invalid `cmap` string'
|
|
839
|
+
if logger: logger.error(err_msg)
|
|
840
|
+
raise ImgplotError(err_msg)
|
|
841
|
+
|
|
842
|
+
if categ:
|
|
843
|
+
# --- Treat categorical variable ---
|
|
844
|
+
if categCol is not None \
|
|
845
|
+
and type(categCol) is not list \
|
|
846
|
+
and type(categCol) is not tuple:
|
|
847
|
+
err_msg = f'{fname}: `categCol` must be a list or a tuple (if not `None`)'
|
|
848
|
+
if logger: logger.error(err_msg)
|
|
849
|
+
raise ImgplotError(err_msg)
|
|
850
|
+
|
|
851
|
+
# Get array 'dval' of displayed values
|
|
852
|
+
if categVal is not None:
|
|
853
|
+
dval = np.array(categVal).reshape(-1) # force to be a 1d array
|
|
854
|
+
|
|
855
|
+
if len(np.unique(dval)) != len(dval):
|
|
856
|
+
err_msg = f'{fname}: `categVal` contains duplicated entries'
|
|
857
|
+
if logger: logger.error(err_msg)
|
|
858
|
+
raise ImgplotError(err_msg)
|
|
859
|
+
|
|
860
|
+
# Check 'categCol' (if not None)
|
|
861
|
+
if categCol is not None and len(categCol) != len(dval):
|
|
862
|
+
err_msg = f'{fname}: length of `categVal` and length of `categCol` differ'
|
|
863
|
+
if logger: logger.error(err_msg)
|
|
864
|
+
raise ImgplotError(err_msg)
|
|
865
|
+
|
|
866
|
+
else:
|
|
867
|
+
# Possibly exclude values from zz
|
|
868
|
+
if excludedVal is not None:
|
|
869
|
+
for val in np.array(excludedVal).reshape(-1):
|
|
870
|
+
np.putmask(zz, zz == val, np.nan)
|
|
871
|
+
|
|
872
|
+
# Get the unique value in zz
|
|
873
|
+
dval = np.array([v for v in np.unique(zz).reshape(-1) if ~np.isnan(v)])
|
|
874
|
+
|
|
875
|
+
if not len(dval) and verbose > 0: # len(dval) == 0
|
|
876
|
+
if logger:
|
|
877
|
+
logger.warning(f'{fname}: no value to be drawn!')
|
|
878
|
+
else:
|
|
879
|
+
print(f'{fname}: WARNING: no value to be drawn!')
|
|
880
|
+
|
|
881
|
+
# Replace dval[i] by i in zz and other values by np.nan
|
|
882
|
+
zz2 = np.array(zz) # copy array
|
|
883
|
+
zz[...] = np.nan # initialize
|
|
884
|
+
for i, v in enumerate(dval):
|
|
885
|
+
zz[zz2 == v] = i
|
|
886
|
+
|
|
887
|
+
del zz2
|
|
888
|
+
|
|
889
|
+
# Set 'colorList': the list of colors to use
|
|
890
|
+
colorList = None
|
|
891
|
+
if categCol is not None:
|
|
892
|
+
if len(categCol) >= len(dval):
|
|
893
|
+
colorList = [categCol[i] for i in range(len(dval))]
|
|
894
|
+
# colorList = [mcolors.ColorConverter().to_rgba(categCol[i]) for i in range(len(dval))]
|
|
895
|
+
|
|
896
|
+
elif categColCycle:
|
|
897
|
+
if verbose > 0:
|
|
898
|
+
if logger:
|
|
899
|
+
logger.warning(f'{fname}: `categCol` is used cyclically (too few entries)')
|
|
900
|
+
else:
|
|
901
|
+
print(f'{fname}: WARNING: `categCol` is used cyclically (too few entries)')
|
|
902
|
+
colorList = [categCol[i%len(categCol)] for i in range(len(dval))]
|
|
903
|
+
|
|
904
|
+
else:
|
|
905
|
+
if verbose > 0:
|
|
906
|
+
if logger:
|
|
907
|
+
logger.warning(f'{fname}: `categCol` not used (too few entries)')
|
|
908
|
+
else:
|
|
909
|
+
print(f'{fname}: WARNING: `categCol` not used (too few entries)')
|
|
910
|
+
|
|
911
|
+
if colorList is None:
|
|
912
|
+
# Use colors from cmap
|
|
913
|
+
colorList = [cmap(x) for x in np.arange(len(dval)) * 1.0/(len(dval)-1)]
|
|
914
|
+
|
|
915
|
+
# Set the colormap: 'cmap'
|
|
916
|
+
if len(dval) == 1:
|
|
917
|
+
cmap = ccol.custom_cmap([colorList[0], colorList[0]], ncol=2,
|
|
918
|
+
cbad=categColbad, alpha=alpha)
|
|
919
|
+
|
|
920
|
+
else: # len(dval) == len(colorList) > 1
|
|
921
|
+
# cmap = mcolors.ListedColormap(colorList)
|
|
922
|
+
cmap = ccol.custom_cmap(colorList, ncol=len(colorList),
|
|
923
|
+
cbad=categColbad, alpha=alpha)
|
|
924
|
+
|
|
925
|
+
# Set the min and max of the colorbar
|
|
926
|
+
vmin, vmax = -0.5, len(dval) - 0.5
|
|
927
|
+
|
|
928
|
+
else: # categ == False
|
|
929
|
+
# --- Treat continuous variable ---
|
|
930
|
+
# Possibly exclude values from zz
|
|
931
|
+
if excludedVal is not None:
|
|
932
|
+
for v in np.array(excludedVal).reshape(-1): # force to be a 1d array
|
|
933
|
+
np.putmask(zz, zz == v, np.nan)
|
|
934
|
+
|
|
935
|
+
if np.all(np.isnan(zz)):
|
|
936
|
+
vmin, vmax= 0.0, 1.0 # any values
|
|
937
|
+
else:
|
|
938
|
+
if vmin is None:
|
|
939
|
+
vmin = np.nanmin(zz)
|
|
940
|
+
|
|
941
|
+
if vmax is None:
|
|
942
|
+
vmax = np.nanmax(zz)
|
|
943
|
+
|
|
944
|
+
col = cmap((zz-vmin)/(vmax-vmin))
|
|
945
|
+
|
|
946
|
+
return col
|
|
947
|
+
# ----------------------------------------------------------------------------
|
|
948
|
+
|
|
949
|
+
# ----------------------------------------------------------------------------
|
|
950
|
+
def drawImage2Drgb(im, nancol=(1.0, 0.0, 0.0), logger=None):
|
|
951
|
+
"""
|
|
952
|
+
Displays a 2D image with 3 or 4 variables interpreted as RGB or RGBA code.
|
|
953
|
+
|
|
954
|
+
Parameters
|
|
955
|
+
----------
|
|
956
|
+
im : :class:`geone.img.Img`
|
|
957
|
+
input image, with `im.nv=3` or `im.nv=4` variables interpreted as RGB or
|
|
958
|
+
RBGA code (normalized in [0, 1])
|
|
959
|
+
|
|
960
|
+
nancol : color, default: (1.0, 0.0, 0.0)
|
|
961
|
+
color (3-tuple for RGB code, 4-tuple for RGBA code, str) used for missing
|
|
962
|
+
value (`numpy.nan`) in the input image
|
|
963
|
+
|
|
964
|
+
logger : :class:`logging.Logger`, optional
|
|
965
|
+
logger (see package `logging`)
|
|
966
|
+
if specified, messages are written via `logger` (no print)
|
|
967
|
+
"""
|
|
968
|
+
fname = 'drawImage2Drgb'
|
|
969
|
+
|
|
970
|
+
# Check image parameters
|
|
971
|
+
if im.nz != 1:
|
|
972
|
+
err_msg = f'{fname}: `im.nz` must be 1'
|
|
973
|
+
if logger: logger.error(err_msg)
|
|
974
|
+
raise ImgplotError(err_msg)
|
|
975
|
+
|
|
976
|
+
if im.nv != 3 and im.nv != 4:
|
|
977
|
+
err_msg = f'{fname}: `im.nv` must be 3 or 4'
|
|
978
|
+
if logger: logger.error(err_msg)
|
|
979
|
+
raise ImgplotError(err_msg)
|
|
980
|
+
|
|
981
|
+
vv = im.val.reshape(im.nv, -1).T
|
|
982
|
+
|
|
983
|
+
if vv.shape[1] == 3:
|
|
984
|
+
nancolf = mcolors.to_rgb(nancol)
|
|
985
|
+
else: # vv.shape[1] == 4
|
|
986
|
+
nancolf = mcolors.to_rgba(nancol)
|
|
987
|
+
|
|
988
|
+
ind_isnan = np.any(np.isnan(vv), axis=1)
|
|
989
|
+
vv[ind_isnan, :] = nancolf
|
|
990
|
+
|
|
991
|
+
min0, max0 = im.ox, im.xmax()
|
|
992
|
+
min1, max1 = im.oy, im.ymax()
|
|
993
|
+
|
|
994
|
+
plt.imshow(vv.reshape(im.ny, im.nx, -1), origin='lower', extent=[min0, max0, min1, max1])
|
|
995
|
+
# ----------------------------------------------------------------------------
|
|
996
|
+
|
|
997
|
+
# ----------------------------------------------------------------------------
|
|
998
|
+
def drawGeobodyMap2D(im, iv=0, logger=None):
|
|
999
|
+
"""
|
|
1000
|
+
Displays a geobody 2D map, with adapted color bar.
|
|
1001
|
+
|
|
1002
|
+
Parameters
|
|
1003
|
+
----------
|
|
1004
|
+
im : :class:`geone.img.Img`
|
|
1005
|
+
input image, with variable of index `iv` interpreted as geobody labels,
|
|
1006
|
+
i.e.:
|
|
1007
|
+
|
|
1008
|
+
- value 0: cell not in the considered medium,
|
|
1009
|
+
- value n > 0: cell in the n-th geobody (connected component)
|
|
1010
|
+
|
|
1011
|
+
iv : int, default: 0
|
|
1012
|
+
index of the variable to be displayed
|
|
1013
|
+
|
|
1014
|
+
logger : :class:`logging.Logger`, optional
|
|
1015
|
+
logger (see package `logging`)
|
|
1016
|
+
if specified, messages are written via `logger` (no print)
|
|
1017
|
+
"""
|
|
1018
|
+
# fname = 'drawGeobodyMap2D'
|
|
1019
|
+
|
|
1020
|
+
categ = True
|
|
1021
|
+
ngeo = int(im.val[iv].max())
|
|
1022
|
+
categVal = [i for i in range(1, ngeo+1)]
|
|
1023
|
+
categCol = None
|
|
1024
|
+
if ngeo <= 10:
|
|
1025
|
+
categCol = plt.get_cmap('tab10').colors[:ngeo]
|
|
1026
|
+
cticks = np.arange(ngeo)
|
|
1027
|
+
cticklabels = 1 + cticks
|
|
1028
|
+
elif ngeo <= 20:
|
|
1029
|
+
categCol = plt.get_cmap('tab20').colors[:ngeo]
|
|
1030
|
+
cticks = np.arange(ngeo)
|
|
1031
|
+
cticklabels = 1 + cticks
|
|
1032
|
+
elif ngeo <= 40:
|
|
1033
|
+
categCol = plt.get_cmap('tab20b').colors + plt.get_cmap('tab20c').colors[:ngeo-20]
|
|
1034
|
+
cticks = np.arange(0,ngeo,5)
|
|
1035
|
+
cticklabels = 1 + cticks
|
|
1036
|
+
else:
|
|
1037
|
+
categ = False
|
|
1038
|
+
cticks = None
|
|
1039
|
+
cticklabels = None
|
|
1040
|
+
drawImage2D(im, iv=iv, excludedVal=0, categ=categ, categVal=categVal, categCol=categCol,
|
|
1041
|
+
cticks=cticks, cticklabels=cticklabels, logger=logger)
|
|
1042
|
+
# ----------------------------------------------------------------------------
|
|
1043
|
+
|
|
1044
|
+
# ----------------------------------------------------------------------------
|
|
1045
|
+
def writeImage2Dppm(
|
|
1046
|
+
im,
|
|
1047
|
+
filename,
|
|
1048
|
+
ix=None, iy=None, iz=None, iv=0,
|
|
1049
|
+
cmap=ccol.cmap_def,
|
|
1050
|
+
excludedVal=None,
|
|
1051
|
+
categ=False, categVal=None, categCol=None,
|
|
1052
|
+
vmin=None, vmax=None,
|
|
1053
|
+
verbose=1,
|
|
1054
|
+
logger=None):
|
|
1055
|
+
"""
|
|
1056
|
+
Writes an image in a file in ppm format.
|
|
1057
|
+
|
|
1058
|
+
The colors according the to given settings, as defined in the function
|
|
1059
|
+
`drawImage2D` are used.
|
|
1060
|
+
|
|
1061
|
+
Parameters
|
|
1062
|
+
----------
|
|
1063
|
+
im : :class:`geone.img.Img`
|
|
1064
|
+
input image
|
|
1065
|
+
|
|
1066
|
+
filename : str
|
|
1067
|
+
name of the file
|
|
1068
|
+
|
|
1069
|
+
ix : see function :func:`drawImage2D`
|
|
1070
|
+
|
|
1071
|
+
iy : see function :func:`drawImage2D`
|
|
1072
|
+
|
|
1073
|
+
iz : see function :func:`drawImage2D`
|
|
1074
|
+
|
|
1075
|
+
iv : see function :func:`drawImage2D`
|
|
1076
|
+
|
|
1077
|
+
cmap : see function :func:`drawImage2D`
|
|
1078
|
+
|
|
1079
|
+
excludedVal : see function :func:`drawImage2D`
|
|
1080
|
+
|
|
1081
|
+
categ : see function :func:`drawImage2D`
|
|
1082
|
+
|
|
1083
|
+
categVal : see function :func:`drawImage2D`
|
|
1084
|
+
|
|
1085
|
+
categCol : see function :func:`drawImage2D`
|
|
1086
|
+
|
|
1087
|
+
vmin : see function :func:`drawImage2D`
|
|
1088
|
+
|
|
1089
|
+
vmax : see function :func:`drawImage2D`
|
|
1090
|
+
|
|
1091
|
+
verbose : int, default: 1
|
|
1092
|
+
verbose mode, higher implies more printing (info)
|
|
1093
|
+
|
|
1094
|
+
logger : :class:`logging.Logger`, optional
|
|
1095
|
+
logger (see package `logging`)
|
|
1096
|
+
if specified, messages are written via `logger` (no print)
|
|
1097
|
+
"""
|
|
1098
|
+
fname = 'writeImage2Dppm'
|
|
1099
|
+
|
|
1100
|
+
# Check iv
|
|
1101
|
+
if iv < 0:
|
|
1102
|
+
iv = im.nv + iv
|
|
1103
|
+
|
|
1104
|
+
if iv < 0 or iv >= im.nv:
|
|
1105
|
+
err_msg = f'{fname}: invalid `iv` index'
|
|
1106
|
+
if logger: logger.error(err_msg)
|
|
1107
|
+
raise ImgplotError(err_msg)
|
|
1108
|
+
|
|
1109
|
+
# Check slice direction and indices
|
|
1110
|
+
n = int(ix is not None) + int(iy is not None) + int(iz is not None)
|
|
1111
|
+
|
|
1112
|
+
if n == 0:
|
|
1113
|
+
sliceDir = 'z'
|
|
1114
|
+
iz = 0
|
|
1115
|
+
|
|
1116
|
+
elif n==1:
|
|
1117
|
+
if ix is not None:
|
|
1118
|
+
if ix < 0:
|
|
1119
|
+
ix = im.nx + ix
|
|
1120
|
+
|
|
1121
|
+
if ix < 0 or ix >= im.nx:
|
|
1122
|
+
err_msg = f'{fname}: invalid `ix` index'
|
|
1123
|
+
if logger: logger.error(err_msg)
|
|
1124
|
+
raise ImgplotError(err_msg)
|
|
1125
|
+
|
|
1126
|
+
sliceDir = 'x'
|
|
1127
|
+
|
|
1128
|
+
elif iy is not None:
|
|
1129
|
+
if iy < 0:
|
|
1130
|
+
iy = im.ny + iy
|
|
1131
|
+
|
|
1132
|
+
if iy < 0 or iy >= im.ny:
|
|
1133
|
+
err_msg = f'{fname}: invalid `iy` index'
|
|
1134
|
+
if logger: logger.error(err_msg)
|
|
1135
|
+
raise ImgplotError(err_msg)
|
|
1136
|
+
|
|
1137
|
+
sliceDir = 'y'
|
|
1138
|
+
|
|
1139
|
+
else: # iz is not None
|
|
1140
|
+
if iz < 0:
|
|
1141
|
+
iz = im.nz + iz
|
|
1142
|
+
|
|
1143
|
+
if iz < 0 or iz >= im.nz:
|
|
1144
|
+
err_msg = f'{fname}: invalid `iz` index'
|
|
1145
|
+
if logger: logger.error(err_msg)
|
|
1146
|
+
raise ImgplotError(err_msg)
|
|
1147
|
+
|
|
1148
|
+
sliceDir = 'z'
|
|
1149
|
+
|
|
1150
|
+
else: # n > 1
|
|
1151
|
+
err_msg = f'{fname}: slice specified in more than one direction'
|
|
1152
|
+
if logger: logger.error(err_msg)
|
|
1153
|
+
raise ImgplotError(err_msg)
|
|
1154
|
+
|
|
1155
|
+
# Extract what to be plotted
|
|
1156
|
+
if sliceDir == 'x':
|
|
1157
|
+
dim0 = im.ny
|
|
1158
|
+
min0 = im.oy
|
|
1159
|
+
max0 = im.ymax()
|
|
1160
|
+
|
|
1161
|
+
dim1 = im.nz
|
|
1162
|
+
min1 = im.oz
|
|
1163
|
+
max1 = im.zmax()
|
|
1164
|
+
zz = np.array(im.val[iv, :, :, ix].reshape(dim1, dim0)) # np.array() to get a copy
|
|
1165
|
+
# reshape to force 2-dimensional array
|
|
1166
|
+
|
|
1167
|
+
elif sliceDir == 'y':
|
|
1168
|
+
dim0 = im.nx
|
|
1169
|
+
min0 = im.ox
|
|
1170
|
+
max0 = im.xmax()
|
|
1171
|
+
|
|
1172
|
+
dim1 = im.nz
|
|
1173
|
+
min1 = im.oz
|
|
1174
|
+
max1 = im.zmax()
|
|
1175
|
+
zz = np.array(im.val[iv, :, iy, :].reshape(dim1, dim0)) # np.array() to get a copy
|
|
1176
|
+
|
|
1177
|
+
else: # sliceDir == 'z'
|
|
1178
|
+
dim0 = im.nx
|
|
1179
|
+
min0 = im.ox
|
|
1180
|
+
max0 = im.xmax()
|
|
1181
|
+
|
|
1182
|
+
dim1 = im.ny
|
|
1183
|
+
min1 = im.oy
|
|
1184
|
+
max1 = im.ymax()
|
|
1185
|
+
zz = np.array(im.val[iv, iz, :, :].reshape(dim1, dim0)) # np.array() to get a copy
|
|
1186
|
+
|
|
1187
|
+
if categ:
|
|
1188
|
+
# --- Treat categorical variable ---
|
|
1189
|
+
if categCol is not None \
|
|
1190
|
+
and type(categCol) is not list \
|
|
1191
|
+
and type(categCol) is not tuple:
|
|
1192
|
+
err_msg = f'{fname}: `categCol` must be a list or a tuple (if not `None`)'
|
|
1193
|
+
if logger: logger.error(err_msg)
|
|
1194
|
+
raise ImgplotError(err_msg)
|
|
1195
|
+
|
|
1196
|
+
# Get array 'dval' of displayed values
|
|
1197
|
+
if categVal is not None:
|
|
1198
|
+
dval = np.array(categVal).reshape(-1) # force to be a 1d array
|
|
1199
|
+
|
|
1200
|
+
if len(np.unique(dval)) != len(dval):
|
|
1201
|
+
err_msg = f'{fname}: `categVal` contains duplicated entries'
|
|
1202
|
+
if logger: logger.error(err_msg)
|
|
1203
|
+
raise ImgplotError(err_msg)
|
|
1204
|
+
|
|
1205
|
+
# Check 'categCol' (if not None)
|
|
1206
|
+
if categCol is not None and len(categCol) != len(dval):
|
|
1207
|
+
err_msg = f'{fname}: length of `categVal` and length of `categCol` differ'
|
|
1208
|
+
if logger: logger.error(err_msg)
|
|
1209
|
+
raise ImgplotError(err_msg)
|
|
1210
|
+
|
|
1211
|
+
else:
|
|
1212
|
+
# Possibly exclude values from zz
|
|
1213
|
+
if excludedVal is not None:
|
|
1214
|
+
for val in np.array(excludedVal).reshape(-1):
|
|
1215
|
+
np.putmask(zz, zz == val, np.nan)
|
|
1216
|
+
|
|
1217
|
+
# Get the unique value in zz
|
|
1218
|
+
dval = np.array([v for v in np.unique(zz).reshape(-1) if ~np.isnan(v)])
|
|
1219
|
+
|
|
1220
|
+
if not len(dval): # len(dval) == 0
|
|
1221
|
+
err_msg = f'{fname}: no value to be drawn' # Warning instead and not raise error...
|
|
1222
|
+
if logger: logger.error(err_msg)
|
|
1223
|
+
raise ImgplotError(err_msg)
|
|
1224
|
+
|
|
1225
|
+
# Replace dval[i] by i in zz and other values by np.nan
|
|
1226
|
+
zz2 = np.array(zz) # copy array
|
|
1227
|
+
zz[...] = np.nan # initialize
|
|
1228
|
+
for i, v in enumerate(dval):
|
|
1229
|
+
zz[zz2 == v] = i
|
|
1230
|
+
|
|
1231
|
+
del zz2
|
|
1232
|
+
|
|
1233
|
+
# Set 'colorList': the list of colors to use
|
|
1234
|
+
colorList = None
|
|
1235
|
+
if categCol is not None:
|
|
1236
|
+
if len(categCol) >= len(dval):
|
|
1237
|
+
colorList = [categCol[i] for i in range(len(dval))]
|
|
1238
|
+
|
|
1239
|
+
else:
|
|
1240
|
+
if verbose > 0:
|
|
1241
|
+
if logger:
|
|
1242
|
+
logger.warning(f'{fname}: `categCol` not used (too few entries)')
|
|
1243
|
+
else:
|
|
1244
|
+
print(f'{fname}: WARNING: `categCol` not used (too few entries)')
|
|
1245
|
+
|
|
1246
|
+
if colorList is None:
|
|
1247
|
+
# Use colors from cmap
|
|
1248
|
+
colorList = [cmap(0)]
|
|
1249
|
+
if len(dval) > 1:
|
|
1250
|
+
t = 1./(len(dval)-1)
|
|
1251
|
+
for i in range(1,len(dval)):
|
|
1252
|
+
colorList.append(cmap(i*t))
|
|
1253
|
+
|
|
1254
|
+
# Set the colormap: 'cmap'
|
|
1255
|
+
if len(dval) == 1:
|
|
1256
|
+
cmap = ccol.custom_cmap([colorList[0], colorList[0]], ncol=2,
|
|
1257
|
+
cbad=ccol.cbad_def)
|
|
1258
|
+
|
|
1259
|
+
else: # len(dval) == len(colorList) > 1
|
|
1260
|
+
# cmap = mcolors.ListedColormap(colorList)
|
|
1261
|
+
cmap = ccol.custom_cmap(colorList, ncol=len(colorList),
|
|
1262
|
+
cbad=ccol.cbad_def)
|
|
1263
|
+
|
|
1264
|
+
# Set the min and max of the colorbar
|
|
1265
|
+
vmin, vmax = -0.5, len(dval) - 0.5
|
|
1266
|
+
|
|
1267
|
+
# Set colorbar ticks and ticklabels if not given
|
|
1268
|
+
if cticks is None:
|
|
1269
|
+
cticks = range(len(dval))
|
|
1270
|
+
|
|
1271
|
+
if cticklabels is None:
|
|
1272
|
+
cticklabels = [f'{v:.3g}' for v in dval]
|
|
1273
|
+
|
|
1274
|
+
# Reset cextend if needed
|
|
1275
|
+
colorbar_extend = 'neither'
|
|
1276
|
+
|
|
1277
|
+
else: # categ == False
|
|
1278
|
+
# --- Treat continuous variable ---
|
|
1279
|
+
# Possibly exclude values from zz
|
|
1280
|
+
if excludedVal is not None:
|
|
1281
|
+
for val in np.array(excludedVal).reshape(-1): # force to be a 1d array
|
|
1282
|
+
np.putmask(zz, zz == val, np.nan)
|
|
1283
|
+
|
|
1284
|
+
# Get dimension of zz, flip zz vertically, then reshape as a list of value
|
|
1285
|
+
ny = zz.shape[0]
|
|
1286
|
+
nx = zz.shape[1]
|
|
1287
|
+
zz = zz[list(reversed(range(zz.shape[0]))),:].reshape(-1)
|
|
1288
|
+
|
|
1289
|
+
# Set vmin and vmax (if needed)
|
|
1290
|
+
if vmin is None:
|
|
1291
|
+
vmin = np.nanmin(zz)
|
|
1292
|
+
|
|
1293
|
+
if vmax is None:
|
|
1294
|
+
vmax = np.nanmax(zz)
|
|
1295
|
+
|
|
1296
|
+
# Get indices of bad, under, over value
|
|
1297
|
+
ind_bad = np.isnan(zz)
|
|
1298
|
+
ind_under = np.all((~ind_bad, zz < vmin),0)
|
|
1299
|
+
ind_over = np.all((~ind_bad, zz > vmax),0)
|
|
1300
|
+
|
|
1301
|
+
# Normalize value according to colorbar
|
|
1302
|
+
zz = np.maximum(np.minimum((zz-vmin)/(vmax-vmin), 1.0), 0.0)
|
|
1303
|
+
|
|
1304
|
+
# Get rgba code at each pixel
|
|
1305
|
+
rgba_arr = np.asarray([cmap(v) for v in zz])
|
|
1306
|
+
|
|
1307
|
+
if cmap._rgba_bad is not None:
|
|
1308
|
+
rgba_arr[ind_bad] = cmap._rgba_bad
|
|
1309
|
+
|
|
1310
|
+
if cmap._rgba_under is not None:
|
|
1311
|
+
rgba_arr[ind_under] = cmap._rgba_under
|
|
1312
|
+
|
|
1313
|
+
if cmap._rgba_over is not None:
|
|
1314
|
+
rgba_arr[ind_over] = cmap._rgba_over
|
|
1315
|
+
|
|
1316
|
+
# Convert rgb from 0-1 to integer in 0-255 (ignored a chanel)
|
|
1317
|
+
zz = np.round(rgba_arr[:,0:3]*255)
|
|
1318
|
+
|
|
1319
|
+
# Write file (ppm)
|
|
1320
|
+
shead = ("P3\n"
|
|
1321
|
+
"# CREATED BY PYTHON3-CODE\n"
|
|
1322
|
+
"{0} {1}\n"
|
|
1323
|
+
"255\n").format(nx, ny) # header of ppm file
|
|
1324
|
+
# Open the file in write binary mode
|
|
1325
|
+
with open(filename,'wb') as ff:
|
|
1326
|
+
ff.write(shead.encode()) # write header
|
|
1327
|
+
# Write rgb values
|
|
1328
|
+
np.savetxt(ff, zz.reshape(-1), fmt='%d')
|
|
1329
|
+
# ----------------------------------------------------------------------------
|
|
1330
|
+
|
|
1331
|
+
if __name__ == "__main__":
|
|
1332
|
+
print("Module 'geone.imgplot' example:")
|
|
1333
|
+
import matplotlib.pyplot as plt
|
|
1334
|
+
|
|
1335
|
+
# Set image with 50 variables
|
|
1336
|
+
# ---------------------------
|
|
1337
|
+
# Set domain and number of cell in each direction
|
|
1338
|
+
xmin, xmax = -2, 2
|
|
1339
|
+
ymin, ymax = -1, 2
|
|
1340
|
+
nx, ny = 200, 150
|
|
1341
|
+
|
|
1342
|
+
# Set the cell size
|
|
1343
|
+
sx, sy = (xmax-xmin)/nx, (ymax-ymin)/ny
|
|
1344
|
+
|
|
1345
|
+
# Set the meshgrid
|
|
1346
|
+
x, y = xmin + 0.5 * sx + sx * np.arange(nx), ymin + 0.5 * sy + sy * np.arange(ny)
|
|
1347
|
+
# # equivalently:
|
|
1348
|
+
# x, y = np.arange(xmin+sx/2, xmax, sx), np.arange(ymin+sy/2, ymax ,sy)
|
|
1349
|
+
# x, y = np.linspace(xmin+sx/2, xmax-sx/2, nx), np.linspace(ymin+sy/2, ymax-sy/2, ny)
|
|
1350
|
+
xx,yy = np.meshgrid(x, y)
|
|
1351
|
+
|
|
1352
|
+
# function values
|
|
1353
|
+
zz = xx**2 + yy**2 - 2
|
|
1354
|
+
|
|
1355
|
+
# Set some values to nan
|
|
1356
|
+
zz[np.where(zz < -1.7)] = np.nan
|
|
1357
|
+
|
|
1358
|
+
# set image, where each variable consists in
|
|
1359
|
+
# the function values 'zz' + a gaussian noise
|
|
1360
|
+
nv = 50
|
|
1361
|
+
im = Img(nx=nx, ny=ny, nz=1, nv=nv,
|
|
1362
|
+
sx=sx, sy=sy, sz=1.0,
|
|
1363
|
+
ox=xmin, oy=ymin, oz=0.0)
|
|
1364
|
+
|
|
1365
|
+
for i in range(nv):
|
|
1366
|
+
im.set_var(ind=i, val=zz.reshape(-1)+np.random.normal(size=im.nxy()))
|
|
1367
|
+
|
|
1368
|
+
# Compute the mean and standard deviation
|
|
1369
|
+
# ---------------------------------------
|
|
1370
|
+
imMean = img.imageContStat(im,op='mean')
|
|
1371
|
+
imStd = img.imageContStat(im,op='std',ddof=1)
|
|
1372
|
+
|
|
1373
|
+
# Draw images
|
|
1374
|
+
# -----------
|
|
1375
|
+
# Set min and max value to be displayed
|
|
1376
|
+
vmin, vmax = -1.0, 3.0
|
|
1377
|
+
|
|
1378
|
+
fig, ax = plt.subplots(2,2,figsize=(12,10))
|
|
1379
|
+
plt.subplot(2,2,1)
|
|
1380
|
+
drawImage2D(im, iv=0, vmin=vmin, vmax=vmax, title='1-st real',
|
|
1381
|
+
colorbar_extend='both')
|
|
1382
|
+
|
|
1383
|
+
plt.subplot(2,2,2)
|
|
1384
|
+
drawImage2D(im, iv=1, vmin=vmin, vmax=vmax, title='2-nd real',
|
|
1385
|
+
colorbar_extend='both')
|
|
1386
|
+
|
|
1387
|
+
plt.subplot(2,2,3)
|
|
1388
|
+
drawImage2D(imMean, vmin=vmin, vmax=vmax,
|
|
1389
|
+
title=f'Mean over {nv} real',
|
|
1390
|
+
colorbar_extend='both')
|
|
1391
|
+
|
|
1392
|
+
plt.subplot(2,2,4)
|
|
1393
|
+
drawImage2D(imStd, title=f'Std over {nv} real')
|
|
1394
|
+
|
|
1395
|
+
# plt.tight_layout()
|
|
1396
|
+
|
|
1397
|
+
# fig.show()
|
|
1398
|
+
plt.show()
|
|
1399
|
+
|
|
1400
|
+
# Copy im and categorize
|
|
1401
|
+
# ----------------------
|
|
1402
|
+
imCat = img.copyImg(im)
|
|
1403
|
+
bins = np.array([-np.inf, -1., 0., 1., np.inf])
|
|
1404
|
+
# set category j to a value v of image im as follows:
|
|
1405
|
+
# bins[j-1] <= v < bins[j], for j=1,...,bins.size-1
|
|
1406
|
+
# and j=np.nan if v is np.nan
|
|
1407
|
+
|
|
1408
|
+
defInd = np.array(~np.isnan(im.val)).reshape(-1) # defined indices
|
|
1409
|
+
|
|
1410
|
+
imCat.val[...] = np.nan # initialization
|
|
1411
|
+
|
|
1412
|
+
for j in range(1, bins.size):
|
|
1413
|
+
# Set category j
|
|
1414
|
+
imCat.val[np.all((defInd,
|
|
1415
|
+
np.asarray(im.val >= bins[j-1]).reshape(-1),
|
|
1416
|
+
np.asarray(im.val < bins[j]).reshape(-1)),
|
|
1417
|
+
0).reshape(imCat.val.shape)] = j
|
|
1418
|
+
|
|
1419
|
+
categ = list(range(1, bins.size))
|
|
1420
|
+
imCatProp = img.imageCategProp(imCat,categ)
|
|
1421
|
+
|
|
1422
|
+
# Draw images
|
|
1423
|
+
# -----------
|
|
1424
|
+
# Set background color to "white"
|
|
1425
|
+
mpl_rcParams['figure.facecolor'] = 'white'
|
|
1426
|
+
|
|
1427
|
+
fig, ax = plt.subplots(2,3,figsize=(18,10))
|
|
1428
|
+
plt.subplot(2,3,1)
|
|
1429
|
+
drawImage2D(imCat, iv=0, categ=True, title='1-st real')
|
|
1430
|
+
|
|
1431
|
+
plt.subplot(2,3,2)
|
|
1432
|
+
# drawImage2D(imCat, iv=1, categ=True, title='2-nd real')
|
|
1433
|
+
drawImage2D(imCat, iv=1, categ=True, title='2-nd real',
|
|
1434
|
+
title_fontsize=18, title_fontweight='bold',
|
|
1435
|
+
xlabel="x-axis", xlabel_fontsize=8,
|
|
1436
|
+
xticks=[-2,0,2], xticklabels_fontsize=8,
|
|
1437
|
+
clabel="facies",clabel_fontsize=16,clabel_rotation=90,
|
|
1438
|
+
cticklabels=['A','B','C','D'],cticklabels_fontsize=8)
|
|
1439
|
+
|
|
1440
|
+
plt.subplot(2,3,3)
|
|
1441
|
+
drawImage2D(imCatProp, iv=0, vmin=0, vmax=0.7, colorbar_extend='max',
|
|
1442
|
+
title=f'Prop. of "{categ[0]}" over {nv} real')
|
|
1443
|
+
|
|
1444
|
+
plt.subplot(2,3,4)
|
|
1445
|
+
drawImage2D(imCatProp, iv=1, vmin=0, vmax=0.7, colorbar_extend='max',
|
|
1446
|
+
title=f'Prop. of "{categ[1]}" over {nv} real')
|
|
1447
|
+
|
|
1448
|
+
plt.subplot(2,3,5)
|
|
1449
|
+
drawImage2D(imCatProp, iv=2, vmin=0, vmax=0.7, colorbar_extend='max',
|
|
1450
|
+
title=f'Prop. of "{categ[2]}" over {nv} real')
|
|
1451
|
+
|
|
1452
|
+
plt.subplot(2,3,6)
|
|
1453
|
+
drawImage2D(imCatProp, iv=3, vmin=0, vmax=0.7, colorbar_extend='max',
|
|
1454
|
+
title=f'Prop. of "{categ[3]}" over {nv} real',
|
|
1455
|
+
cticks=np.arange(0,.8,.1), cticklabels=[f'{i:4.2f}' for i in np.arange(0,.8,.1)],
|
|
1456
|
+
cticklabels_fontweight='bold')
|
|
1457
|
+
|
|
1458
|
+
plt.suptitle('Categorized images...')
|
|
1459
|
+
# plt.tight_layout()
|
|
1460
|
+
|
|
1461
|
+
# fig.show()
|
|
1462
|
+
plt.show()
|
|
1463
|
+
|
|
1464
|
+
a = input("Press enter to continue...")
|