matplotlib-map-utils 1.0.3__py3-none-any.whl → 2.0.0__py3-none-any.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.
- matplotlib_map_utils/__init__.py +5 -1
- matplotlib_map_utils/core/__init__.py +4 -0
- matplotlib_map_utils/{north_arrow.py → core/north_arrow.py} +56 -55
- matplotlib_map_utils/core/scale_bar.py +1200 -0
- matplotlib_map_utils/defaults/__init__.py +4 -0
- matplotlib_map_utils/{defaults.py → defaults/north_arrow.py} +4 -4
- matplotlib_map_utils/defaults/scale_bar.py +377 -0
- matplotlib_map_utils/validation/__init__.py +2 -0
- matplotlib_map_utils/validation/functions.py +231 -0
- matplotlib_map_utils/validation/north_arrow.py +175 -0
- matplotlib_map_utils/validation/scale_bar.py +274 -0
- matplotlib_map_utils-2.0.0.dist-info/METADATA +281 -0
- matplotlib_map_utils-2.0.0.dist-info/RECORD +18 -0
- {matplotlib_map_utils-1.0.3.dist-info → matplotlib_map_utils-2.0.0.dist-info}/WHEEL +1 -1
- matplotlib_map_utils/validation.py +0 -332
- matplotlib_map_utils-1.0.3.dist-info/METADATA +0 -131
- matplotlib_map_utils-1.0.3.dist-info/RECORD +0 -11
- {matplotlib_map_utils-1.0.3.dist-info → matplotlib_map_utils-2.0.0.dist-info}/LICENSE +0 -0
- {matplotlib_map_utils-1.0.3.dist-info → matplotlib_map_utils-2.0.0.dist-info}/top_level.txt +0 -0
@@ -1,8 +1,8 @@
|
|
1
|
-
|
2
|
-
# defaults.py contains default values for the
|
1
|
+
#####################################################################
|
2
|
+
# defaults/north_arrow.py contains default values for the NorthArrows
|
3
3
|
# at difference plot sizes (xsmall to xlarge)
|
4
4
|
# see their corresponding sizes under each default heading
|
5
|
-
|
5
|
+
#####################################################################
|
6
6
|
|
7
7
|
# The main variables that update are the following:
|
8
8
|
# base: scale, linewidth
|
@@ -407,7 +407,7 @@ _AOB_XL = {
|
|
407
407
|
|
408
408
|
## CONTAINER
|
409
409
|
# This makes an easy-to-call dictionary of all the defaults we've set, for easy unpacking by the set_size function
|
410
|
-
|
410
|
+
_DEFAULTS_NA = {
|
411
411
|
"xs":[_SCALE_XS, _BASE_XS, _FANCY_XS, _LABEL_XS, _SHADOW_XS, _PACK_XS, _AOB_XS],
|
412
412
|
"sm":[_SCALE_SM, _BASE_SM, _FANCY_SM, _LABEL_SM, _SHADOW_SM, _PACK_SM, _AOB_SM],
|
413
413
|
"md":[_SCALE_MD, _BASE_MD, _FANCY_MD, _LABEL_MD, _SHADOW_MD, _PACK_MD, _AOB_MD],
|
@@ -0,0 +1,377 @@
|
|
1
|
+
#################################################################
|
2
|
+
# defaults/scale_bar.py contains default values for the ScaleBars
|
3
|
+
# at difference plot sizes (xsmall to xlarge)
|
4
|
+
# see their corresponding sizes under each default heading
|
5
|
+
#################################################################
|
6
|
+
|
7
|
+
# The main variables that update are the following:
|
8
|
+
# bar: height, edgewidth, tickwidth
|
9
|
+
# labels: sep, style
|
10
|
+
# units: sep
|
11
|
+
# text: fontsize, stroke_width (also changes labels and units)
|
12
|
+
# aob: pad, borderpad
|
13
|
+
|
14
|
+
## X-SMALL DEFAULTS
|
15
|
+
# Should work well for ~A8ish paper (2 to 3 inches, or 5 to 8 cm)
|
16
|
+
# The arrow will appear to be ~1/10 of an inch in height
|
17
|
+
|
18
|
+
# Bar
|
19
|
+
_BAR_XS = {
|
20
|
+
"projection":None,
|
21
|
+
"unit":None,
|
22
|
+
"rotation":0,
|
23
|
+
"max":None,
|
24
|
+
"length":None,
|
25
|
+
"height":0.05, # changed
|
26
|
+
"reverse":False,
|
27
|
+
"major_div":None,
|
28
|
+
"minor_div":None,
|
29
|
+
"minor_frac":0.66,
|
30
|
+
"minor_type":"none",
|
31
|
+
"facecolors":["black","white"],
|
32
|
+
"edgecolors":"black",
|
33
|
+
"edgewidth":0.5, # changed
|
34
|
+
"tick_loc":"above",
|
35
|
+
"basecolors":["black"],
|
36
|
+
"tickcolors":["black"],
|
37
|
+
"tickwidth":0.5 # changed
|
38
|
+
}
|
39
|
+
|
40
|
+
# Labels
|
41
|
+
_LABELS_XS = {
|
42
|
+
"labels":None,
|
43
|
+
"format":".2f",
|
44
|
+
"format_int":True,
|
45
|
+
"style":"first_last", # changed
|
46
|
+
"loc":"above",
|
47
|
+
"pad":0,
|
48
|
+
"sep":1.5, # changed
|
49
|
+
}
|
50
|
+
|
51
|
+
# Units
|
52
|
+
_UNITS_XS = {
|
53
|
+
"label":None,
|
54
|
+
"loc":"bar",
|
55
|
+
"pad":0,
|
56
|
+
"sep":1.5, # changed
|
57
|
+
}
|
58
|
+
|
59
|
+
# Text
|
60
|
+
_TEXT_XS = {
|
61
|
+
"fontsize":4, # changed
|
62
|
+
"textcolor":"black",
|
63
|
+
"fontfamily":"sans-serif",
|
64
|
+
"fontstyle":"normal",
|
65
|
+
"fontweight":"regular",
|
66
|
+
"stroke_width":0.5, # changed
|
67
|
+
"stroke_color":"white",
|
68
|
+
"rotation":None,
|
69
|
+
"rotation_mode":"anchor",
|
70
|
+
}
|
71
|
+
|
72
|
+
# AOB
|
73
|
+
_AOB_XS = {
|
74
|
+
"facecolor":None,
|
75
|
+
"edgecolor":None,
|
76
|
+
"alpha":None,
|
77
|
+
"pad":0.1, # changed
|
78
|
+
"borderpad":0.1, # changed
|
79
|
+
"prop":"medium",
|
80
|
+
"frameon":False,
|
81
|
+
"bbox_to_anchor":None,
|
82
|
+
"bbox_transform":None
|
83
|
+
}
|
84
|
+
|
85
|
+
## SMALL DEFAULTS
|
86
|
+
# Should work well for ~A6 paper (4 to 6 inches, or 11 to 15 cm)
|
87
|
+
# The arrow will appear to be ~1/4 of an inch in height
|
88
|
+
|
89
|
+
# Bar
|
90
|
+
_BAR_SM = {
|
91
|
+
"projection":None,
|
92
|
+
"unit":None,
|
93
|
+
"rotation":0,
|
94
|
+
"max":None,
|
95
|
+
"length":None,
|
96
|
+
"height":0.075, # changed
|
97
|
+
"reverse":False,
|
98
|
+
"major_div":None,
|
99
|
+
"minor_div":None,
|
100
|
+
"minor_frac":0.66,
|
101
|
+
"minor_type":"none",
|
102
|
+
"facecolors":["black","white"],
|
103
|
+
"edgecolors":"black",
|
104
|
+
"edgewidth":0.75, # changed
|
105
|
+
"tick_loc":"above",
|
106
|
+
"basecolors":["black"],
|
107
|
+
"tickcolors":["black"],
|
108
|
+
"tickwidth":0.75 # changed
|
109
|
+
}
|
110
|
+
|
111
|
+
# Labels
|
112
|
+
_LABELS_SM = {
|
113
|
+
"labels":None,
|
114
|
+
"format":".2f",
|
115
|
+
"format_int":True,
|
116
|
+
"style":"first_last", # changed
|
117
|
+
"loc":"above",
|
118
|
+
"pad":0,
|
119
|
+
"sep":3, # changed
|
120
|
+
}
|
121
|
+
|
122
|
+
# Units
|
123
|
+
_UNITS_SM = {
|
124
|
+
"label":None,
|
125
|
+
"loc":"bar",
|
126
|
+
"pad":0,
|
127
|
+
"sep":3, # changed
|
128
|
+
}
|
129
|
+
|
130
|
+
# Text
|
131
|
+
_TEXT_SM = {
|
132
|
+
"fontsize":6, # changed
|
133
|
+
"textcolor":"black",
|
134
|
+
"fontfamily":"sans-serif",
|
135
|
+
"fontstyle":"normal",
|
136
|
+
"fontweight":"regular",
|
137
|
+
"stroke_width":0.5, # changed
|
138
|
+
"stroke_color":"white",
|
139
|
+
"rotation":None,
|
140
|
+
"rotation_mode":"anchor",
|
141
|
+
}
|
142
|
+
|
143
|
+
# AOB
|
144
|
+
_AOB_SM = {
|
145
|
+
"facecolor":None,
|
146
|
+
"edgecolor":None,
|
147
|
+
"alpha":None,
|
148
|
+
"pad":0.33, # changed
|
149
|
+
"borderpad":0.33, # changed
|
150
|
+
"prop":"medium",
|
151
|
+
"frameon":False,
|
152
|
+
"bbox_to_anchor":None,
|
153
|
+
"bbox_transform":None
|
154
|
+
}
|
155
|
+
|
156
|
+
## MEDIUM DEFAULTS
|
157
|
+
# Should work well for ~A4/Letter paper (8 to 12 inches, or 21 to 30 cm)
|
158
|
+
# The arrow will appear to be ~ 1/2 an inch or ~1 cm in height
|
159
|
+
|
160
|
+
# Bar
|
161
|
+
_BAR_MD = {
|
162
|
+
"projection":None,
|
163
|
+
"unit":None,
|
164
|
+
"rotation":0,
|
165
|
+
"max":None,
|
166
|
+
"length":None,
|
167
|
+
"height":0.1, # changed
|
168
|
+
"reverse":False,
|
169
|
+
"major_div":None,
|
170
|
+
"minor_div":None,
|
171
|
+
"minor_frac":0.66,
|
172
|
+
"minor_type":"first",
|
173
|
+
"facecolors":["black","white"],
|
174
|
+
"edgecolors":"black",
|
175
|
+
"edgewidth":1, # changed
|
176
|
+
"tick_loc":"above",
|
177
|
+
"basecolors":["black"],
|
178
|
+
"tickcolors":["black"],
|
179
|
+
"tickwidth":1.5 # changed
|
180
|
+
}
|
181
|
+
|
182
|
+
# Labels
|
183
|
+
_LABELS_MD = {
|
184
|
+
"labels":None,
|
185
|
+
"format":".2f",
|
186
|
+
"format_int":True,
|
187
|
+
"style":"major",
|
188
|
+
"loc":"above",
|
189
|
+
"pad":0,
|
190
|
+
"sep":5, # changed
|
191
|
+
}
|
192
|
+
|
193
|
+
# Units
|
194
|
+
_UNITS_MD = {
|
195
|
+
"label":None,
|
196
|
+
"loc":"bar",
|
197
|
+
"pad":0,
|
198
|
+
"sep":5, # changed
|
199
|
+
}
|
200
|
+
|
201
|
+
# Text
|
202
|
+
_TEXT_MD = {
|
203
|
+
"fontsize":12, # changed
|
204
|
+
"textcolor":"black",
|
205
|
+
"fontfamily":"sans-serif",
|
206
|
+
"fontstyle":"normal",
|
207
|
+
"fontweight":"regular",
|
208
|
+
"stroke_width":1, # changed
|
209
|
+
"stroke_color":"white",
|
210
|
+
"rotation":None,
|
211
|
+
"rotation_mode":"anchor",
|
212
|
+
}
|
213
|
+
|
214
|
+
# AOB
|
215
|
+
_AOB_MD = {
|
216
|
+
"facecolor":None,
|
217
|
+
"edgecolor":None,
|
218
|
+
"alpha":None,
|
219
|
+
"pad":0.5, # changed
|
220
|
+
"borderpad":0.5, # changed
|
221
|
+
"prop":"medium",
|
222
|
+
"frameon":False,
|
223
|
+
"bbox_to_anchor":None,
|
224
|
+
"bbox_transform":None
|
225
|
+
}
|
226
|
+
|
227
|
+
## LARGE DEFAULTS
|
228
|
+
# Should work well for ~A2 paper (16 to 24 inches, or 42 to 60 cm)
|
229
|
+
# The arrow will appear to be ~an inch in height
|
230
|
+
|
231
|
+
# Bar
|
232
|
+
_BAR_LG = {
|
233
|
+
"projection":None,
|
234
|
+
"unit":None,
|
235
|
+
"rotation":0,
|
236
|
+
"max":None,
|
237
|
+
"length":None,
|
238
|
+
"height":0.2, # changed
|
239
|
+
"reverse":False,
|
240
|
+
"major_div":None,
|
241
|
+
"minor_div":None,
|
242
|
+
"minor_frac":0.66,
|
243
|
+
"minor_type":"first",
|
244
|
+
"facecolors":["black","white"],
|
245
|
+
"edgecolors":"black",
|
246
|
+
"edgewidth":2, # changed
|
247
|
+
"tick_loc":"above",
|
248
|
+
"basecolors":["black"],
|
249
|
+
"tickcolors":["black"],
|
250
|
+
"tickwidth":3 # changed
|
251
|
+
}
|
252
|
+
|
253
|
+
# Labels
|
254
|
+
_LABELS_LG = {
|
255
|
+
"labels":None,
|
256
|
+
"format":".2f",
|
257
|
+
"format_int":True,
|
258
|
+
"style":"major",
|
259
|
+
"loc":"above",
|
260
|
+
"pad":0,
|
261
|
+
"sep":8, # changed
|
262
|
+
}
|
263
|
+
|
264
|
+
# Units
|
265
|
+
_UNITS_LG = {
|
266
|
+
"label":None,
|
267
|
+
"loc":"bar",
|
268
|
+
"pad":0,
|
269
|
+
"sep":8, # changed
|
270
|
+
}
|
271
|
+
|
272
|
+
# Text
|
273
|
+
_TEXT_LG = {
|
274
|
+
"fontsize":24, # changed
|
275
|
+
"textcolor":"black",
|
276
|
+
"fontfamily":"sans-serif",
|
277
|
+
"fontstyle":"normal",
|
278
|
+
"fontweight":"regular",
|
279
|
+
"stroke_width":2, # changed
|
280
|
+
"stroke_color":"white",
|
281
|
+
"rotation":None,
|
282
|
+
"rotation_mode":"anchor",
|
283
|
+
}
|
284
|
+
|
285
|
+
# AOB
|
286
|
+
_AOB_LG = {
|
287
|
+
"facecolor":None,
|
288
|
+
"edgecolor":None,
|
289
|
+
"alpha":None,
|
290
|
+
"pad":1, # changed
|
291
|
+
"borderpad":1, # changed
|
292
|
+
"prop":"medium",
|
293
|
+
"frameon":False,
|
294
|
+
"bbox_to_anchor":None,
|
295
|
+
"bbox_transform":None
|
296
|
+
}
|
297
|
+
|
298
|
+
## X-LARGE DEFAULTS
|
299
|
+
# Should work well for ~A0/Poster paper (33 to 47 inches, or 85 to 120 cm)
|
300
|
+
# The arrow will appear to be ~2 inches in height
|
301
|
+
|
302
|
+
# Bar
|
303
|
+
_BAR_XL = {
|
304
|
+
"projection":None,
|
305
|
+
"unit":None,
|
306
|
+
"rotation":0,
|
307
|
+
"max":None,
|
308
|
+
"length":None,
|
309
|
+
"height":0.4, # changed
|
310
|
+
"reverse":False,
|
311
|
+
"major_div":None,
|
312
|
+
"minor_div":None,
|
313
|
+
"minor_frac":0.66,
|
314
|
+
"minor_type":"first",
|
315
|
+
"facecolors":["black","white"],
|
316
|
+
"edgecolors":"black",
|
317
|
+
"edgewidth":4, # changed
|
318
|
+
"tick_loc":"above",
|
319
|
+
"basecolors":["black"],
|
320
|
+
"tickcolors":["black"],
|
321
|
+
"tickwidth":5 # changed
|
322
|
+
}
|
323
|
+
|
324
|
+
# Labels
|
325
|
+
_LABELS_XL = {
|
326
|
+
"labels":None,
|
327
|
+
"format":".2f",
|
328
|
+
"format_int":True,
|
329
|
+
"style":"major",
|
330
|
+
"loc":"above",
|
331
|
+
"pad":0,
|
332
|
+
"sep":12, # changed
|
333
|
+
}
|
334
|
+
|
335
|
+
# Units
|
336
|
+
_UNITS_XL = {
|
337
|
+
"label":None,
|
338
|
+
"loc":"bar",
|
339
|
+
"pad":0,
|
340
|
+
"sep":12, # changed
|
341
|
+
}
|
342
|
+
|
343
|
+
# Text
|
344
|
+
_TEXT_XL = {
|
345
|
+
"fontsize":48, # changed
|
346
|
+
"textcolor":"black",
|
347
|
+
"fontfamily":"sans-serif",
|
348
|
+
"fontstyle":"normal",
|
349
|
+
"fontweight":"regular",
|
350
|
+
"stroke_width":4, # changed
|
351
|
+
"stroke_color":"white",
|
352
|
+
"rotation":None,
|
353
|
+
"rotation_mode":"anchor",
|
354
|
+
}
|
355
|
+
|
356
|
+
# AOB
|
357
|
+
_AOB_XL = {
|
358
|
+
"facecolor":None,
|
359
|
+
"edgecolor":None,
|
360
|
+
"alpha":None,
|
361
|
+
"pad":2, # changed
|
362
|
+
"borderpad":2, # changed
|
363
|
+
"prop":"medium",
|
364
|
+
"frameon":False,
|
365
|
+
"bbox_to_anchor":None,
|
366
|
+
"bbox_transform":None
|
367
|
+
}
|
368
|
+
|
369
|
+
## CONTAINER
|
370
|
+
# This makes an easy-to-call dictionary of all the defaults we've set, for easy unpacking by the set_size function
|
371
|
+
_DEFAULTS_SB = {
|
372
|
+
"xs":[_BAR_XS, _LABELS_XS, _UNITS_XS, _TEXT_XS, _AOB_XS],
|
373
|
+
"sm":[_BAR_SM, _LABELS_SM, _UNITS_SM, _TEXT_SM, _AOB_SM],
|
374
|
+
"md":[_BAR_MD, _LABELS_MD, _UNITS_MD, _TEXT_MD, _AOB_MD],
|
375
|
+
"lg":[_BAR_LG, _LABELS_LG, _UNITS_LG, _TEXT_LG, _AOB_LG],
|
376
|
+
"xl":[_BAR_XL, _LABELS_XL, _UNITS_XL, _TEXT_XL, _AOB_XL],
|
377
|
+
}
|
@@ -0,0 +1,231 @@
|
|
1
|
+
# Default packages
|
2
|
+
import warnings
|
3
|
+
# Geo packages
|
4
|
+
import pyproj
|
5
|
+
# matplotlib's useful validation functions
|
6
|
+
import matplotlib.rcsetup
|
7
|
+
|
8
|
+
### VALIDITY CHECKS ###
|
9
|
+
# Functions and variables used for validating inputs for classes
|
10
|
+
# All have a similar form, taking in the name of the property (prop), the value (val)
|
11
|
+
# some parameters to check against (min/max, list, type, etc.),
|
12
|
+
# and whether or not None is acceptable value
|
13
|
+
|
14
|
+
def _validate_list(prop, val, list, none_ok=False):
|
15
|
+
if none_ok==False and val is None:
|
16
|
+
raise ValueError(f"None is not a valid value for {prop}, please provide a value in this list: {list}")
|
17
|
+
elif none_ok==True and val is None:
|
18
|
+
return val
|
19
|
+
elif not val in list:
|
20
|
+
raise ValueError(f"'{val}' is not a valid value for {prop}, please provide a value in this list: {list}")
|
21
|
+
return val
|
22
|
+
|
23
|
+
def _validate_range(prop, val, min, max, none_ok=False):
|
24
|
+
if none_ok==False and val is None:
|
25
|
+
raise ValueError(f"None is not a valid value for {prop}, please provide a value between {min} and {max}")
|
26
|
+
elif none_ok==True and val is None:
|
27
|
+
return val
|
28
|
+
elif type(val) != int and type(val) != float:
|
29
|
+
raise ValueError(f"The supplied type is not valid for {prop}, please provide a float or integer between {min} and {max}")
|
30
|
+
elif max is not None:
|
31
|
+
if not val >= min and not val <= max:
|
32
|
+
raise ValueError(f"'{val}' is not a valid value for {prop}, please provide a value between {min} and {max}")
|
33
|
+
elif max is None:
|
34
|
+
if not val >= min:
|
35
|
+
raise ValueError(f"'{val}' is not a valid value for {prop}, please provide a value greater than {min}")
|
36
|
+
return val
|
37
|
+
|
38
|
+
def _validate_type(prop, val, match, none_ok=False):
|
39
|
+
if none_ok==False and val is None:
|
40
|
+
raise ValueError(f"None is not a valid value for {prop}, please provide an object of type {match}")
|
41
|
+
elif none_ok==True and val is None:
|
42
|
+
return val
|
43
|
+
elif not type(val)==match:
|
44
|
+
raise ValueError(f"'{val}' is not a valid value for {prop}, please provide an object of type {match}")
|
45
|
+
return val
|
46
|
+
|
47
|
+
def _validate_types(prop, val, matches, none_ok=False):
|
48
|
+
if none_ok==False and val is None:
|
49
|
+
raise ValueError(f"None is not a valid value for {prop}, please provide an object of type {matches}")
|
50
|
+
elif none_ok==True and val is None:
|
51
|
+
return val
|
52
|
+
elif not type(val) in matches:
|
53
|
+
raise ValueError(f"'{val}' is not a valid value for {prop}, please provide an object of type {matches}")
|
54
|
+
return val
|
55
|
+
|
56
|
+
def _validate_coords(prop, val, numpy_type, dims, none_ok=False):
|
57
|
+
if none_ok==False and val is None:
|
58
|
+
raise ValueError(f"None is not a valid value for {prop}, please provide an object of type {numpy_type}")
|
59
|
+
elif none_ok==True and val is None:
|
60
|
+
return val
|
61
|
+
elif not type(val)==numpy_type:
|
62
|
+
raise ValueError(f"'{val}' is not a valid value for {prop}, please provide an object of type {numpy_type}")
|
63
|
+
elif not val.ndim==dims:
|
64
|
+
raise ValueError(f"'{val}' is not a valid value for {prop}, please provide a numpy array with {dims} dimensions")
|
65
|
+
return val
|
66
|
+
|
67
|
+
def _validate_tuple(prop, val, length, types, none_ok=False):
|
68
|
+
if none_ok==False and val is None:
|
69
|
+
raise ValueError(f"None is not a valid value for {prop}, please provide a tuple of length {length} instead")
|
70
|
+
elif none_ok==True and val is None:
|
71
|
+
return val
|
72
|
+
elif type(val)!=tuple:
|
73
|
+
raise ValueError(f"{val} is not a valid value for {prop}, please provide a tuple of length {length} instead")
|
74
|
+
elif len(val)!=length:
|
75
|
+
raise ValueError(f"{val} is not a valid value for {prop}, please provide a tuple of length {length} instead")
|
76
|
+
else:
|
77
|
+
for item in val:
|
78
|
+
if type(item) not in types:
|
79
|
+
raise ValueError(f"{type(item)} is not a valid value for the items in {prop}, please provide a value of one of the following types: {types}")
|
80
|
+
return val
|
81
|
+
|
82
|
+
def _validate_color_or_none(prop, val, none_ok=False):
|
83
|
+
if none_ok==False and val is None:
|
84
|
+
raise ValueError(f"None is not a valid value for {prop}, please provide a color string acceptable to matplotlib instead")
|
85
|
+
elif none_ok==True and val is None:
|
86
|
+
return val
|
87
|
+
else:
|
88
|
+
matplotlib.rcsetup.validate_color(val)
|
89
|
+
return val
|
90
|
+
|
91
|
+
# NOTE: This one is a bit messy, particularly with the rotation module, but I can't think of a better way to do it...
|
92
|
+
def _validate_crs(prop, val, rotation_dict, none_ok=False):
|
93
|
+
degrees = rotation_dict.get("degrees",None)
|
94
|
+
crs = rotation_dict.get("crs",None)
|
95
|
+
reference = rotation_dict.get("reference",None)
|
96
|
+
coords = rotation_dict.get("coords",None)
|
97
|
+
|
98
|
+
if degrees is None:
|
99
|
+
if reference == "center":
|
100
|
+
if crs is None:
|
101
|
+
raise ValueError(f"If degrees is set to None, and reference is 'center', then a valid crs must be supplied")
|
102
|
+
else:
|
103
|
+
if crs is None or reference is None or coords is None:
|
104
|
+
raise ValueError(f"If degrees is set to None, then crs, reference, and coords cannot be None: please provide a valid input for each of these variables instead")
|
105
|
+
elif (type(degrees)==int or type(degrees)==float) and (crs is not None or reference is not None or coords is not None):
|
106
|
+
warnings.warn(f"A value for degrees was supplied; values for crs, reference, and coords will be ignored")
|
107
|
+
return val
|
108
|
+
else:
|
109
|
+
if none_ok==False and val is None:
|
110
|
+
raise ValueError(f"If degrees is set to None, then {prop} cannot be None: please provide a valid CRS input for PyProj instead")
|
111
|
+
elif none_ok==True and val is None:
|
112
|
+
return val
|
113
|
+
# This happens if (a) a value for CRS is supplied and (b) a value for degrees is NOT supplied
|
114
|
+
if type(val)==pyproj.CRS:
|
115
|
+
pass
|
116
|
+
else:
|
117
|
+
try:
|
118
|
+
val = pyproj.CRS.from_user_input(val)
|
119
|
+
except:
|
120
|
+
raise Exception(f"Invalid CRS supplied ({val}), please provide a valid CRS input for PyProj instead")
|
121
|
+
return val
|
122
|
+
|
123
|
+
# A simpler validation function for CRSs
|
124
|
+
def _validate_projection(prop, val, none_ok=False):
|
125
|
+
if type(val)==pyproj.CRS:
|
126
|
+
pass
|
127
|
+
else:
|
128
|
+
try:
|
129
|
+
val = pyproj.CRS.from_user_input(val)
|
130
|
+
except:
|
131
|
+
raise Exception(f"Invalid CRS supplied ({val}), please provide a valid CRS input for PyProj instead")
|
132
|
+
return val
|
133
|
+
|
134
|
+
# It is specifically to apply another validation function to the items in a list
|
135
|
+
# Ex. if we want to validate a LIST of colors instead of a single color
|
136
|
+
def _validate_iterable(prop, val, func, kwargs=None):
|
137
|
+
# Making sure we wrap everything in a list
|
138
|
+
if type(val) not in [tuple, list]:
|
139
|
+
val = [val]
|
140
|
+
# Then, we apply our validation func with optional kwargs to each item in said list, relying on it to return an error value
|
141
|
+
if kwargs is not None:
|
142
|
+
for v in val:
|
143
|
+
v = func(prop=prop, val=v, **kwargs)
|
144
|
+
return val
|
145
|
+
# The matplotlib built-in functions DON'T have that, and only ever take the one value
|
146
|
+
else:
|
147
|
+
for v in val:
|
148
|
+
v = func(v)
|
149
|
+
return val
|
150
|
+
|
151
|
+
# This is specifically to apply multiple validation functions to a value, if needed
|
152
|
+
# Ex. If an item can be a string OR a list of strings, we can use this to validate it
|
153
|
+
# "labels":{"func":_validate_multiple, "kwargs":{"funcs":[_validate_type, _validate_list], "kwargs":[{"match":str, "none_ok":True}, {"list":["major","first_last","minor_all","minor_first"], "none_ok":True}]}}, # any string or any item in the list
|
154
|
+
def _validate_multiple(prop, val, funcs, kwargs):
|
155
|
+
# Simply iterate through each func and kwarg
|
156
|
+
for f,k in zip(funcs,kwargs):
|
157
|
+
# We wrap the attempts in a try block to suppress the errors
|
158
|
+
try:
|
159
|
+
v = f(prop=prop, val=v, **k)
|
160
|
+
# If we pass, we can stop here and return the value
|
161
|
+
return val
|
162
|
+
except:
|
163
|
+
continue
|
164
|
+
# If we didn't return a value and exit the loop yet, then the passed value is incorrect, as we raise an error
|
165
|
+
raise ValueError(f"{val} is not a valid value for {prop}, please provide check the documentation")
|
166
|
+
|
167
|
+
# This final one is used for keys that are not validated
|
168
|
+
def _skip_validation(val, none_ok=False):
|
169
|
+
return val
|
170
|
+
|
171
|
+
|
172
|
+
### MORE VALIDITY FUNCTIONS ###
|
173
|
+
# These are more customized, and so are separated from the _validate_* functions above
|
174
|
+
# Mainly, they can process the input dictionaries wholesale, as well as the individual functions in it
|
175
|
+
def _validate_dict(input_dict, default_dict, functions, to_validate=None, return_clean=False, parse_false=True):
|
176
|
+
if input_dict == False:
|
177
|
+
if parse_false == True:
|
178
|
+
return None
|
179
|
+
else:
|
180
|
+
return False
|
181
|
+
elif input_dict is None or input_dict == True:
|
182
|
+
return default_dict
|
183
|
+
elif type(input_dict) != dict:
|
184
|
+
raise ValueError(f"A dictionary (NoneType) must be provided, please double-check your inputs")
|
185
|
+
else:
|
186
|
+
values = default_dict | input_dict
|
187
|
+
# Pre-checking that no invalid keys are passed
|
188
|
+
invalid = [key for key in values.keys() if key not in functions.keys() and key not in ["bbox_to_anchor", "bbox_transform"]]
|
189
|
+
if len(invalid) > 0:
|
190
|
+
print(f"Warning: Invalid keys detected ({invalid}). These will be ignored.")
|
191
|
+
# First, trimming our values to only those we need to validate
|
192
|
+
if to_validate == "input":
|
193
|
+
values = {key: val for key, val in values.items() if (key in input_dict.keys() and key in functions.keys())} # have to check against both here
|
194
|
+
functions = {key: val for key, val in functions.items() if key in values.keys()}
|
195
|
+
elif to_validate is not None:
|
196
|
+
values = {key: val for key, val in values.items() if key in to_validate}
|
197
|
+
functions = {key: val for key, val in functions.items() if key in values.keys()}
|
198
|
+
else:
|
199
|
+
values = {key: val for key, val in values.items() if key in functions.keys()}
|
200
|
+
functions = {key: val for key, val in functions.items() if key in values.keys()}
|
201
|
+
# Now, running the function with the necessary kwargs
|
202
|
+
for key,val in values.items():
|
203
|
+
fd = functions[key]
|
204
|
+
func = fd["func"]
|
205
|
+
# NOTE: This is messy but the only way to get the rotation value to the crs function
|
206
|
+
if key=="crs":
|
207
|
+
_ = func(prop=key, val=val, rotation_dict=values, **fd["kwargs"])
|
208
|
+
# NOTE: This is messy but the only way to check the projection dict without keywords
|
209
|
+
elif key=="projection":
|
210
|
+
_ = func(prop=key, val=val)
|
211
|
+
# Our custom functions always have this dictionary key in them, so we know what form they take
|
212
|
+
elif "kwargs" in fd:
|
213
|
+
_ = func(prop=key, val=val, **fd["kwargs"])
|
214
|
+
# The matplotlib built-in functions DON'T have that, and only ever take the one value
|
215
|
+
else:
|
216
|
+
_ = func(val)
|
217
|
+
if return_clean==True:
|
218
|
+
return values
|
219
|
+
|
220
|
+
# This function can process the _VALIDATE dictionaries we established above, but for single variables at a time
|
221
|
+
def _validate(validate_dict, prop, val, return_val=True, kwargs={}):
|
222
|
+
fd = validate_dict[prop]
|
223
|
+
func = fd["func"]
|
224
|
+
# Our custom functions always have this dictionary key in them, so we know what form they take
|
225
|
+
if "kwargs" in fd:
|
226
|
+
val = func(prop=prop, val=val, **(fd["kwargs"] | kwargs))
|
227
|
+
# The matplotlib built-in functions DON'T have that, and only ever take the one value
|
228
|
+
else:
|
229
|
+
val = func(val)
|
230
|
+
if return_val==True:
|
231
|
+
return val
|