1+ # -*- coding: utf-8 -*-
2+ """
3+ Created on Fri Feb 28 08:53:00 2025
4+
5+ @author: dvezinet
6+ """
7+
8+
9+ import numpy as np
10+ import matplotlib .pyplot as plt
11+ import matplotlib .colors as mcolors
12+ import datastock as ds
13+
14+
15+ # ###############################################################
16+ # ###############################################################
17+ # Main
18+ # ###############################################################
19+
20+
21+ def main (
22+ coll = None ,
23+ data = None ,
24+ dcolor = None ,
25+ # options
26+ color_default = None ,
27+ vmin = None ,
28+ vmax = None ,
29+ log = None ,
30+ ):
31+
32+ # ------------------
33+ # check inputs
34+ # ------------------
35+
36+ data , dcolor , color_default , vmin , vmax , log = _check (
37+ coll = coll ,
38+ data = data ,
39+ dcolor = dcolor ,
40+ color_default = color_default ,
41+ vmin = vmin ,
42+ vmax = vmax ,
43+ log = log ,
44+ )
45+
46+ # ------------------
47+ # initialize
48+ # ------------------
49+
50+ shape = data .shape + (4 ,)
51+ color = np .zeros (shape , dtype = float )
52+
53+ # ------------------
54+ # compute - alpha
55+ # ------------------
56+
57+ if log is True :
58+ vmin = np .log10 (vmin )
59+ vmax = np .log10 (vmax )
60+
61+ alpha = (np .log10 (data ) - vmin ) / (vmax - vmin )
62+
63+ else :
64+ alpha = (data - vmin ) / (vmax - vmin )
65+
66+ # ------------------
67+ # compute - colors
68+ # ------------------
69+
70+ for k0 , v0 in dcolor .items ():
71+
72+ sli = (v0 ['ind' ], slice (0 , 3 ))
73+ color [sli ] = v0 ['color' ]
74+
75+ sli = tuple ([slice (None ) for ii in range (data .ndim )] + [- 1 ])
76+ color [sli ] = alpha
77+
78+ # ------------------
79+ # output
80+ # ------------------
81+
82+ lcol = set ([v0 ['color' ] for v0 in dcolor .values ()])
83+ dcolor = {
84+ 'color' : color ,
85+ 'meaning' : {
86+ kc : [k0 for k0 , v0 in dcolor .items () if v0 ['color' ] == kc ]
87+ for kc in lcol
88+ },
89+ }
90+
91+ return dcolor
92+
93+
94+ # ###############################################################
95+ # ###############################################################
96+ # check
97+ # ###############################################################
98+
99+
100+ def _check (
101+ coll = None ,
102+ data = None ,
103+ dcolor = None ,
104+ # options
105+ color_default = None ,
106+ vmin = None ,
107+ vmax = None ,
108+ log = None ,
109+ ):
110+
111+ # ------------------
112+ # data
113+ # ------------------
114+
115+ lc = [
116+ isinstance (data , np .ndarray ),
117+ isinstance (data , str ) and data in coll .ddata .keys (),
118+ ]
119+ if lc [0 ]:
120+ pass
121+ elif lc [1 ]:
122+ data = coll .ddata [data ]['data' ]
123+ else :
124+ msg = (
125+ "Arg data must be a np.ndarray or a key to an existing data!\n "
126+ f"Provided: { data } \n "
127+ )
128+ raise Exception (msg )
129+
130+
131+ # ------------------
132+ # dcolor
133+ # ------------------
134+
135+ # --------------------
136+ # dcolor format check
137+
138+ c0 = (
139+ isinstance (dcolor , dict )
140+ and all ([
141+ isinstance (k0 , str )
142+ and isinstance (v0 , dict )
143+ and sorted (v0 .keys ()) == ['color' , 'ind' ]
144+ for k0 , v0 in dcolor .items ()
145+ ])
146+ )
147+ if not c0 :
148+ msg = (
149+ "Arg dcolor must be a dict of sub-dicts of shape:\n "
150+ "\t - 'key0': {'ind': ..., 'color': ...}\n "
151+ "\t - ...\n "
152+ "\t - 'keyN': {'ind': ..., 'color': ...}\n "
153+ f"Provided:\n { dcolor } \n "
154+ )
155+ raise Exception (msg )
156+
157+ # --------------------
158+ # ind and color checks
159+
160+ dfail = {}
161+ shape = data .shape
162+ for k0 , v0 in dcolor .items ():
163+
164+ c0 = (
165+ isinstance (v0 ['ind' ], np .ndarray )
166+ and v0 ['ind' ].shape == data .shape
167+ and v0 ['ind' ].dtype == bool
168+ )
169+ if not c0 :
170+ msg = f"'ind' must be a { shape } bool array, not { v0 ['ind' ]} "
171+ dfail [k0 ] = (msg ,)
172+
173+ if not mcolors .is_color_like (v0 ['color' ]):
174+ msg = f"'color' must be color-like, not { v0 ['color' ]} "
175+ if k0 in dfail :
176+ dfail [k0 ] = dfail [k0 ] + (msg ,)
177+ else :
178+ dfail [k0 ] = (msg ,)
179+
180+ # raise exception
181+ if len (dfail ) > 0 :
182+ lmax = np .max ([len (f"\t - { k0 } : " ) for k0 in dfail .keys ()])
183+ lstr = [
184+ f"\t - { k0 } :\n " .ljust (lmax ) + '\n ' .join ([
185+ "" .ljust (lmax + 4 ) + f"\t - { v1 } " .rjust (lmax )
186+ for ii , v1 in enumerate (v0 )
187+ ])
188+ for k0 , v0 in dfail .items ()
189+ ]
190+ msg = (
191+ "Arg dcolor, the following keys have incorrect keys / values:\n "
192+ + "\n " .join (lstr )
193+ )
194+ raise Exception (msg )
195+
196+ # ----------------------
197+ # format colors to rgb
198+
199+ dcol = {}
200+ for k0 , v0 in dcolor .items ():
201+ if np .any (v0 ['ind' ]):
202+ dcol [k0 ] = {
203+ 'ind' : v0 ['ind' ],
204+ 'color' : mcolors .to_rgb (v0 ['color' ]),
205+ }
206+
207+ # ------------------
208+ # color_default
209+ # ------------------
210+
211+ if color_default is None :
212+ color_default = 'k'
213+ if not mcolors .is_color_like (color_default ):
214+ msg = (
215+ "Arg color_default must be color-like!\n "
216+ f"Provided: { color_default } \n "
217+ )
218+ raise Exception (msg )
219+
220+ color_default = mcolors .to_rgb (color_default )
221+
222+ # ------------------
223+ # vmin, vmax
224+ # ------------------
225+
226+ vmin0 = np .nanmin (data )
227+ vmax0 = np .nanmax (data )
228+
229+ # vmin
230+ if vmin is None :
231+ vmin = vmin0
232+ c0 = (np .isscalar (vmin ) and np .isfinite (vmin ) and vmin < vmax0 )
233+ if not c0 :
234+ msg = (
235+ f"Arg vmin must be a finite scalar below max ({ vmax0 } )\n "
236+ f"Provided: { vmin } \n "
237+ )
238+ raise Exception (msg )
239+
240+ # vmax
241+ if vmax is None :
242+ vmax = vmax0
243+ c0 = (np .isscalar (vmax ) and np .isfinite (vmax ) and vmax > vmin0 )
244+ if not c0 :
245+ msg = (
246+ f"Arg vmax must be a finite scalar above min ({ vmin0 } )\n "
247+ f"Provided: { vmax } \n "
248+ )
249+ raise Exception (msg )
250+
251+ # ordering
252+ if vmin >= vmax :
253+ msg = (
254+ "Arg vmin must be below vmax!\n "
255+ f"Provided:\n \t - vmin = { vmin } \n \t - vmax = { vmax } \n "
256+ )
257+ raise Exception (msg )
258+
259+ # ------------------
260+ # log
261+ # ------------------
262+
263+ log = ds ._generic_check ._check_var (
264+ log , 'log' ,
265+ types = bool ,
266+ default = False ,
267+ )
268+
269+ return data , dcol , color_default , vmin , vmax , log
0 commit comments