matplotlib-map-utils 2.1.0__py3-none-any.whl → 3.0.1__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 +16 -1
- matplotlib_map_utils/core/__init__.py +5 -1
- matplotlib_map_utils/core/inset_map.py +952 -0
- matplotlib_map_utils/core/north_arrow.py +0 -4
- matplotlib_map_utils/core/scale_bar.py +16 -11
- matplotlib_map_utils/defaults/inset_map.py +67 -0
- matplotlib_map_utils/validation/functions.py +51 -15
- matplotlib_map_utils/validation/inset_map.py +88 -0
- matplotlib_map_utils/validation/north_arrow.py +1 -1
- matplotlib_map_utils/validation/scale_bar.py +3 -5
- {matplotlib_map_utils-2.1.0.dist-info → matplotlib_map_utils-3.0.1.dist-info}/METADATA +121 -20
- matplotlib_map_utils-3.0.1.dist-info/RECORD +24 -0
- {matplotlib_map_utils-2.1.0.dist-info → matplotlib_map_utils-3.0.1.dist-info}/WHEEL +1 -1
- matplotlib_map_utils-2.1.0.dist-info/RECORD +0 -21
- {matplotlib_map_utils-2.1.0.dist-info → matplotlib_map_utils-3.0.1.dist-info}/licenses/LICENSE +0 -0
- {matplotlib_map_utils-2.1.0.dist-info → matplotlib_map_utils-3.0.1.dist-info}/top_level.txt +0 -0
@@ -15,14 +15,10 @@ import numpy
|
|
15
15
|
import cartopy
|
16
16
|
import pyproj
|
17
17
|
# Graphical packages
|
18
|
-
import matplotlib
|
19
18
|
import matplotlib.artist
|
20
|
-
import matplotlib.pyplot
|
21
19
|
import matplotlib.patches
|
22
20
|
import matplotlib.patheffects
|
23
21
|
import matplotlib.offsetbox
|
24
|
-
# matplotlib's useful validation functions
|
25
|
-
import matplotlib.rcsetup
|
26
22
|
# The types we use in this script
|
27
23
|
from typing import Literal
|
28
24
|
# The information contained in our helper scripts (validation and defaults)
|
@@ -9,27 +9,21 @@
|
|
9
9
|
import warnings
|
10
10
|
import math
|
11
11
|
import copy
|
12
|
-
import re
|
13
12
|
# Math packages
|
14
13
|
import numpy
|
15
14
|
# Geo packages
|
16
|
-
import cartopy
|
17
15
|
import pyproj
|
18
16
|
from great_circle_calculator.great_circle_calculator import distance_between_points
|
19
17
|
# Graphical packages
|
20
18
|
import PIL.Image
|
21
|
-
import matplotlib
|
22
19
|
import matplotlib.artist
|
23
20
|
import matplotlib.lines
|
24
21
|
import matplotlib.pyplot
|
25
22
|
import matplotlib.patches
|
26
23
|
import matplotlib.patheffects
|
27
24
|
import matplotlib.offsetbox
|
28
|
-
import matplotlib.transforms
|
29
25
|
import matplotlib.font_manager
|
30
26
|
from matplotlib.backends.backend_agg import FigureCanvasAgg
|
31
|
-
# matplotlib's useful validation functions
|
32
|
-
import matplotlib.rcsetup
|
33
27
|
# The types we use in this script
|
34
28
|
from typing import Literal
|
35
29
|
# The information contained in our helper scripts (validation and defaults)
|
@@ -286,6 +280,13 @@ def scale_bar(ax, draw=True, style: Literal["ticks","boxes"]="boxes",
|
|
286
280
|
##### CONFIG #####
|
287
281
|
|
288
282
|
# Getting the config for the bar (length, text, divs, etc.)
|
283
|
+
if draw:
|
284
|
+
ax.get_figure().draw_without_rendering()
|
285
|
+
|
286
|
+
# window = ax.get_window_extent()
|
287
|
+
# tight = ax.patch.get_tightbbox()
|
288
|
+
# print(f"{draw} Window: width {round(window.width,2)} ({round(ax.get_figure().dpi_scale_trans.inverted().transform([window.width,0])[0],2)}), height {round(window.height,2)} ({round(ax.get_figure().dpi_scale_trans.inverted().transform([0,window.height])[1],2)})")
|
289
|
+
# print(f"{draw} Tight: width {round(tight.width,2)} ({round(ax.get_figure().dpi_scale_trans.inverted().transform([tight.width,0])[0],2)}), height {round(tight.height,2)} ({round(ax.get_figure().dpi_scale_trans.inverted().transform([0,tight.height])[1],2)})")
|
289
290
|
bar_max, bar_length, units_label, major_div, minor_div = _config_bar(ax, _bar)
|
290
291
|
|
291
292
|
# Getting the config for the segments (width, label, etc.)
|
@@ -458,7 +459,6 @@ def scale_bar(ax, draw=True, style: Literal["ticks","boxes"]="boxes",
|
|
458
459
|
|
459
460
|
# AOB will contain the final artist
|
460
461
|
aob_box = matplotlib.offsetbox.AnchoredOffsetbox(loc="center", child=major_pack, frameon=False, pad=0, borderpad=0)
|
461
|
-
|
462
462
|
# Function that will handle invisibly rendering our object, returning an image
|
463
463
|
img_scale_bar = _render_as_image(fig_temp, ax_temp, aob_box, _bar["rotation"])
|
464
464
|
|
@@ -647,6 +647,11 @@ def _config_bar(ax, bar):
|
|
647
647
|
# Literally just getting the figure for the passed axis
|
648
648
|
|
649
649
|
fig = ax.get_figure()
|
650
|
+
# fig.draw_without_rendering()
|
651
|
+
# Sets the canvas for the figure to AGG (Anti-Grain Geometry)
|
652
|
+
# canvas = FigureCanvasAgg(fig)
|
653
|
+
# Draws the figure onto the canvas
|
654
|
+
# canvas.draw()
|
650
655
|
|
651
656
|
## ROTATION ##
|
652
657
|
# Calculating if the rotation is vertical or horizontal
|
@@ -657,15 +662,15 @@ def _config_bar(ax, bar):
|
|
657
662
|
# Finding the max length and optimal divisions of the scale bar
|
658
663
|
|
659
664
|
# Finding the dimensions of the axis and the limits
|
660
|
-
#
|
665
|
+
# get_window_extent() returns values in pixel coordinates
|
661
666
|
# so dividing by dpi gets us the inches of the axis
|
662
667
|
# Vertical scale bars are oriented against the y-axis (height)
|
663
668
|
if bar_vertical==True:
|
664
|
-
ax_dim = ax.patch.
|
669
|
+
ax_dim = ax.patch.get_window_extent().height / fig.dpi
|
665
670
|
min_lim, max_lim = ax.get_ylim()
|
666
671
|
# Horizontal scale bars are oriented against the x-axis (width)
|
667
672
|
else:
|
668
|
-
ax_dim = ax.patch.
|
673
|
+
ax_dim = ax.patch.get_window_extent().width / fig.dpi
|
669
674
|
min_lim, max_lim = ax.get_xlim()
|
670
675
|
# This calculates the range from max to min on the axis of interest
|
671
676
|
ax_range = abs(max_lim - min_lim)
|
@@ -1017,7 +1022,7 @@ def _temp_figure(ax, axis=False, visible=False):
|
|
1017
1022
|
# Getting the figure of the provided axis
|
1018
1023
|
fig = ax.get_figure()
|
1019
1024
|
# Getting the dimensions of the axis
|
1020
|
-
ax_bbox = ax.patch.
|
1025
|
+
ax_bbox = ax.patch.get_window_extent()
|
1021
1026
|
# Converting to inches and rounding up
|
1022
1027
|
ax_dim = math.ceil(max(ax_bbox.height, ax_bbox.width) / fig.dpi)
|
1023
1028
|
# Creating a new temporary figure
|
@@ -0,0 +1,67 @@
|
|
1
|
+
#################################################################
|
2
|
+
# defaults/inset_map.py contains default values for the InsetMaps
|
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
|
+
# inset map: size, pad
|
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
|
+
|
17
|
+
# Map
|
18
|
+
_INSET_MAP_XS = {
|
19
|
+
"size":0.5,
|
20
|
+
"pad":0.05,
|
21
|
+
}
|
22
|
+
|
23
|
+
## SMALL DEFAULTS
|
24
|
+
# Should work well for ~A6 paper (4 to 6 inches, or 11 to 15 cm)
|
25
|
+
|
26
|
+
# Map
|
27
|
+
_INSET_MAP_SM = {
|
28
|
+
"size":1,
|
29
|
+
"pad":0.1,
|
30
|
+
}
|
31
|
+
|
32
|
+
## MEDIUM DEFAULTS
|
33
|
+
# Should work well for ~A4/Letter paper (8 to 12 inches, or 21 to 30 cm)
|
34
|
+
|
35
|
+
# Map
|
36
|
+
_INSET_MAP_MD = {
|
37
|
+
"size":2,
|
38
|
+
"pad":0.25,
|
39
|
+
}
|
40
|
+
|
41
|
+
## LARGE DEFAULTS
|
42
|
+
# Should work well for ~A2 paper (16 to 24 inches, or 42 to 60 cm)
|
43
|
+
|
44
|
+
# Map
|
45
|
+
_INSET_MAP_LG = {
|
46
|
+
"size":4,
|
47
|
+
"pad":0.5,
|
48
|
+
}
|
49
|
+
|
50
|
+
## X-LARGE DEFAULTS
|
51
|
+
# Should work well for ~A0/Poster paper (33 to 47 inches, or 85 to 120 cm)
|
52
|
+
|
53
|
+
# Map
|
54
|
+
_INSET_MAP_XL = {
|
55
|
+
"size":8,
|
56
|
+
"pad":1,
|
57
|
+
}
|
58
|
+
|
59
|
+
## CONTAINER
|
60
|
+
# This makes an easy-to-call dictionary of all the defaults we've set, for easy unpacking by the set_size function
|
61
|
+
_DEFAULTS_IM = {
|
62
|
+
"xs":[_INSET_MAP_XS],
|
63
|
+
"sm":[_INSET_MAP_SM],
|
64
|
+
"md":[_INSET_MAP_MD],
|
65
|
+
"lg":[_INSET_MAP_LG],
|
66
|
+
"xl":[_INSET_MAP_XL],
|
67
|
+
}
|
@@ -20,7 +20,7 @@ def _validate_list(prop, val, list, none_ok=False):
|
|
20
20
|
raise ValueError(f"'{val}' is not a valid value for {prop}, please provide a value in this list: {list}")
|
21
21
|
return val
|
22
22
|
|
23
|
-
def _validate_range(prop, val, min, max, none_ok=False):
|
23
|
+
def _validate_range(prop, val, min, max=None, none_ok=False):
|
24
24
|
if none_ok==False and val is None:
|
25
25
|
raise ValueError(f"None is not a valid value for {prop}, please provide a value between {min} and {max}")
|
26
26
|
elif none_ok==True and val is None:
|
@@ -69,7 +69,7 @@ def _validate_tuple(prop, val, length, types, none_ok=False):
|
|
69
69
|
raise ValueError(f"None is not a valid value for {prop}, please provide a tuple of length {length} instead")
|
70
70
|
elif none_ok==True and val is None:
|
71
71
|
return val
|
72
|
-
elif
|
72
|
+
elif not isinstance(val, (tuple, list)):
|
73
73
|
raise ValueError(f"{val} is not a valid value for {prop}, please provide a tuple of length {length} instead")
|
74
74
|
elif len(val)!=length:
|
75
75
|
raise ValueError(f"{val} is not a valid value for {prop}, please provide a tuple of length {length} instead")
|
@@ -117,7 +117,7 @@ def _validate_crs(prop, val, rotation_dict, none_ok=False):
|
|
117
117
|
try:
|
118
118
|
val = pyproj.CRS.from_user_input(val)
|
119
119
|
except:
|
120
|
-
raise Exception(f"Invalid CRS supplied ({val}), please provide a valid CRS input
|
120
|
+
raise Exception(f"Invalid CRS supplied ({val}), please provide a valid CRS input that PyProj can use instead")
|
121
121
|
return val
|
122
122
|
|
123
123
|
# A simpler validation function for CRSs
|
@@ -128,14 +128,14 @@ def _validate_projection(prop, val, none_ok=False):
|
|
128
128
|
try:
|
129
129
|
val = pyproj.CRS.from_user_input(val)
|
130
130
|
except:
|
131
|
-
raise Exception(f"Invalid CRS supplied ({val}), please provide a valid CRS input
|
131
|
+
raise Exception(f"Invalid CRS supplied ({val}) for {prop}, please provide a valid CRS input that PyProj can use instead")
|
132
132
|
return val
|
133
133
|
|
134
|
-
#
|
134
|
+
# This is specifically to apply another validation function to the items in a list
|
135
135
|
# Ex. if we want to validate a LIST of colors instead of a single color
|
136
136
|
def _validate_iterable(prop, val, func, kwargs=None):
|
137
137
|
# Making sure we wrap everything in a list
|
138
|
-
if
|
138
|
+
if not isinstance(val, (tuple, list)):
|
139
139
|
val = [val]
|
140
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
141
|
if kwargs is not None:
|
@@ -148,21 +148,57 @@ def _validate_iterable(prop, val, func, kwargs=None):
|
|
148
148
|
v = func(v)
|
149
149
|
return val
|
150
150
|
|
151
|
-
# This is
|
151
|
+
# This is to check for the structure of a dictionary-like object
|
152
|
+
def _validate_keys(prop, val, keys, none_ok=False):
|
153
|
+
if none_ok==False and val is None:
|
154
|
+
raise ValueError(f"None is not a valid value for {prop}, please provide a dictionary with keys {keys} instead")
|
155
|
+
elif none_ok==True and val is None:
|
156
|
+
return val
|
157
|
+
elif not isinstance(val, (dict)):
|
158
|
+
raise ValueError(f"{val} is not a valid value for {prop}, please provide a dictionary with keys {keys} instead")
|
159
|
+
else:
|
160
|
+
for k in val.keys():
|
161
|
+
if k not in keys:
|
162
|
+
raise ValueError(f"{k} is not a valid key for the items in {prop}, please provide a dictionary with keys {keys} instead")
|
163
|
+
return val
|
164
|
+
|
165
|
+
# This is to apply multiple validation functions to a value, if needed - only one needs to pass
|
152
166
|
# Ex. If an item can be a string OR a list of strings, we can use this to validate it
|
153
|
-
|
154
|
-
|
167
|
+
def _validate_or(prop, val, funcs, kwargs):
|
168
|
+
success = False
|
155
169
|
# Simply iterate through each func and kwarg
|
156
170
|
for f,k in zip(funcs,kwargs):
|
157
171
|
# We wrap the attempts in a try block to suppress the errors
|
158
172
|
try:
|
159
|
-
|
173
|
+
val = f(prop=prop, val=val, **k)
|
160
174
|
# If we pass, we can stop here and return the value
|
161
|
-
|
175
|
+
success = True
|
176
|
+
break
|
162
177
|
except:
|
163
|
-
|
164
|
-
|
165
|
-
|
178
|
+
pass
|
179
|
+
if success == False:
|
180
|
+
# If we didn't return a value and exit the loop yet, then the passed value is incorrect, as we raise an error
|
181
|
+
raise ValueError(f"{val} is not a valid value for {prop}, please check the documentation")
|
182
|
+
else:
|
183
|
+
return val
|
184
|
+
|
185
|
+
# This is the same, but ALL need to pass
|
186
|
+
def _validate_and(prop, val, funcs, kwargs):
|
187
|
+
success = True
|
188
|
+
# Simply iterate through each func and kwarg
|
189
|
+
for f,k in zip(funcs,kwargs):
|
190
|
+
# We wrap the attempts in a try block to suppress the errors
|
191
|
+
try:
|
192
|
+
val = f(prop=prop, val=val, **k)
|
193
|
+
except:
|
194
|
+
# If we fail, we can stop here and return the value
|
195
|
+
success = False
|
196
|
+
break
|
197
|
+
if success == False:
|
198
|
+
# If we didn't return a value and exit the loop yet, then the passed value is incorrect, as we raise an error
|
199
|
+
raise ValueError(f"{val} is not a valid value for {prop}, please check the documentation")
|
200
|
+
else:
|
201
|
+
return val
|
166
202
|
|
167
203
|
# This final one is used for keys that are not validated
|
168
204
|
def _skip_validation(val, none_ok=False):
|
@@ -221,7 +257,7 @@ def _validate_dict(input_dict, default_dict, functions, to_validate=None, return
|
|
221
257
|
def _validate(validate_dict, prop, val, return_val=True, kwargs={}):
|
222
258
|
fd = validate_dict[prop]
|
223
259
|
func = fd["func"]
|
224
|
-
#
|
260
|
+
# Most of our custom functions always have this dictionary key in them, so we know what form they take
|
225
261
|
if "kwargs" in fd:
|
226
262
|
val = func(prop=prop, val=val, **(fd["kwargs"] | kwargs))
|
227
263
|
# The matplotlib built-in functions DON'T have that, and only ever take the one value
|
@@ -0,0 +1,88 @@
|
|
1
|
+
############################################################
|
2
|
+
# validation/inset_map.py contains all the main objects
|
3
|
+
# for checking inputs passed to class definitions
|
4
|
+
############################################################
|
5
|
+
|
6
|
+
### IMPORTING PACKAGES ###
|
7
|
+
|
8
|
+
# Geo packages
|
9
|
+
import matplotlib.axes
|
10
|
+
import pyproj
|
11
|
+
# Graphical packages
|
12
|
+
import matplotlib
|
13
|
+
# matplotlib's useful validation functions
|
14
|
+
import matplotlib.rcsetup
|
15
|
+
# The types we use in this script
|
16
|
+
from typing import TypedDict, Literal
|
17
|
+
# Finally, the validation functions
|
18
|
+
from . import functions as vf
|
19
|
+
|
20
|
+
### ALL ###
|
21
|
+
# This code tells other packages what to import if not explicitly stated
|
22
|
+
__all__ = [
|
23
|
+
"_TYPE_INSET", "_VALIDATE_INSET",
|
24
|
+
"_TYPE_EXTENT", "_VALIDATE_EXTENT",
|
25
|
+
"_TYPE_DETAIL", "_VALIDATE_DETAIL",
|
26
|
+
]
|
27
|
+
|
28
|
+
### TYPE HINTS ###
|
29
|
+
# This section of the code is for defining structured dictionaries and lists
|
30
|
+
# for the inputs necessary for object creation we've created (such as the style dictionaries)
|
31
|
+
# so that intellisense can help with autocompletion
|
32
|
+
|
33
|
+
class _TYPE_INSET(TypedDict, total=False):
|
34
|
+
size: int | float | tuple[int | float, int | float] | list[int | float, int | float] # each int or float should be between 0 and inf
|
35
|
+
pad: int | float | tuple[int | float, int | float] | list[int | float, int | float] # each int or float should be between 0 and inf
|
36
|
+
coords: tuple[int | float, int | float] | list[int | float, int | float] # each int or float should be between -inf and inf
|
37
|
+
|
38
|
+
class _TYPE_EXTENT(TypedDict, total=False):
|
39
|
+
pax: matplotlib.axes.Axes # any Matplotlib Axes
|
40
|
+
bax: matplotlib.axes.Axes # any Matplotlib Axes
|
41
|
+
pcrs: str | int | pyproj.CRS # should be a valid cartopy or pyproj crs, or a string or int that can be converted to that
|
42
|
+
bcrs: str | int | pyproj.CRS # should be a valid cartopy or pyproj crs, or a string or int that can be converted to that
|
43
|
+
straighten: bool # either true or false
|
44
|
+
pad: float | int # any positive float or integer
|
45
|
+
plot: bool # either true or false
|
46
|
+
to_return: Literal["shape","patch","fig","ax"] | None # any item in the list, or None if nothing should be returned
|
47
|
+
facecolor: str # a color to use for the face of the box
|
48
|
+
linecolor: str # a color to use for the edge of the box
|
49
|
+
alpha: float | int # any positive float or integer
|
50
|
+
linewidth: float | int # any positive float or integer
|
51
|
+
|
52
|
+
class _TYPE_DETAIL(TypedDict, total=False):
|
53
|
+
to_return: Literal["connectors", "lines"] | None # any item in the list, or None if nothing should be returned
|
54
|
+
connector_color: str # a color to use for the face of the box
|
55
|
+
connector_width: float | int # any positive float or integer
|
56
|
+
|
57
|
+
### VALIDITY DICTS ###
|
58
|
+
# These compile the functions in validation/functions, as well as matplotlib's built-in validity functions
|
59
|
+
# into dictionaries that can be used to validate all the inputs to a dictionary at once
|
60
|
+
|
61
|
+
_VALIDATE_INSET = {
|
62
|
+
"location":{"func":vf._validate_list, "kwargs":{"list":["upper right", "upper left", "lower left", "lower right", "center left", "center right", "lower center", "upper center", "center"]}},
|
63
|
+
"size":{"func":vf._validate_or, "kwargs":{"funcs":[vf._validate_range, vf._validate_and], "kwargs":[{"min":0, "none_ok":True}, {"funcs":[vf._validate_tuple, vf._validate_iterable], "kwargs":[{"length":2, "types":[float, int]}, {"func":vf._validate_range, "kwargs":{"min":0}}]}]}}, # between 0 and inf, or a two-tuple of (x,y) size, each between 0 and inf
|
64
|
+
"pad":{"func":vf._validate_or, "kwargs":{"funcs":[vf._validate_range, vf._validate_and], "kwargs":[{"min":0, "none_ok":True}, {"funcs":[vf._validate_tuple, vf._validate_iterable], "kwargs":[{"length":2, "types":[float, int]}, {"func":vf._validate_range, "kwargs":{"min":0}}]}]}}, # between 0 and inf, or a two-tuple of (x,y) size, each between 0 and inf
|
65
|
+
"coords":{"func":vf._validate_tuple, "kwargs":{"length":2, "types":[float, int], "none_ok":True}}, # a two-tuple of coordinates where you want to place the inset map
|
66
|
+
"to_plot":{"func":vf._validate_iterable, "kwargs":{"func":vf._validate_keys, "kwargs":{"keys":["data","kwargs"], "none_ok":True}}}, # a list of dictionaries, where each contains "data" and "kwargs" keys
|
67
|
+
}
|
68
|
+
|
69
|
+
_VALIDATE_EXTENT = {
|
70
|
+
"pax":{"func":vf._validate_type, "kwargs":{"match":matplotlib.axes.Axes}}, # any Matplotlib Axes
|
71
|
+
"bax":{"func":vf._validate_type, "kwargs":{"match":matplotlib.axes.Axes}}, # any Matplotlib Axes
|
72
|
+
"pcrs":{"func":vf._validate_projection, "kwargs":{"none_ok":False}}, # any valid projection input for PyProj
|
73
|
+
"bcrs":{"func":vf._validate_projection, "kwargs":{"none_ok":False}}, # any valid projection input for PyProj
|
74
|
+
"straighten":{"func":vf._validate_type, "kwargs":{"match":bool}}, # true or false
|
75
|
+
"pad":{"func":vf._validate_range, "kwargs":{"min":0}}, # any positive number
|
76
|
+
"plot":{"func":vf._validate_type, "kwargs":{"match":bool}}, # true or false
|
77
|
+
"facecolor":{"func":matplotlib.rcsetup.validate_color}, # any color value for matplotlib
|
78
|
+
"linecolor":{"func":matplotlib.rcsetup.validate_color}, # any color value for matplotlib
|
79
|
+
"alpha":{"func":vf._validate_range, "kwargs":{"min":0}}, # any positive number
|
80
|
+
"linewidth":{"func":vf._validate_range, "kwargs":{"min":0}}, # any positive number
|
81
|
+
"to_return":{"func":vf._validate_list, "kwargs":{"list":["shape", "patch", "fig", "ax"], "none_ok":True}}, # any value in this list
|
82
|
+
}
|
83
|
+
|
84
|
+
_VALIDATE_DETAIL = {
|
85
|
+
"to_return":{"func":vf._validate_list, "kwargs":{"list":["connectors", "lines"], "none_ok":True}}, # any value in this list
|
86
|
+
"connector_color":{"func":matplotlib.rcsetup.validate_color}, # any color value for matplotlib
|
87
|
+
"connector_width":{"func":vf._validate_range, "kwargs":{"min":0}}, # any positive number
|
88
|
+
}
|
@@ -90,7 +90,7 @@ class _TYPE_ROTATION(TypedDict, total=False):
|
|
90
90
|
coords: Tuple[float | int, float | int] # only required if degrees is None: should be a tuple of coordinates in the relevant reference window
|
91
91
|
|
92
92
|
### VALIDITY DICTS ###
|
93
|
-
# These compile the functions
|
93
|
+
# These compile the functions in validation/functions, as well as matplotlib's built-in validity functions
|
94
94
|
# into dictionaries that can be used to validate all the inputs to a dictionary at once
|
95
95
|
|
96
96
|
_VALIDATE_PRIMARY = {
|
@@ -7,12 +7,10 @@
|
|
7
7
|
|
8
8
|
# Geo packages
|
9
9
|
import pyproj
|
10
|
-
# Graphical packages
|
11
|
-
import matplotlib
|
12
10
|
# matplotlib's useful validation functions
|
13
11
|
import matplotlib.rcsetup
|
14
12
|
# The types we use in this script
|
15
|
-
from typing import
|
13
|
+
from typing import TypedDict, Literal, get_args
|
16
14
|
# Finally, the validation functions
|
17
15
|
from . import functions as vf
|
18
16
|
|
@@ -161,7 +159,7 @@ class _TYPE_AOB(TypedDict, total=False):
|
|
161
159
|
# bbox_transform: None # NOTE: currently unvalidated, use at your own risk!
|
162
160
|
|
163
161
|
### VALIDITY DICTS ###
|
164
|
-
# These compile the functions
|
162
|
+
# These compile the functions in validation/functions, as well as matplotlib's built-in validity functions
|
165
163
|
# into dictionaries that can be used to validate all the inputs to a dictionary at once
|
166
164
|
|
167
165
|
_VALIDATE_PRIMARY = {
|
@@ -173,7 +171,7 @@ _VALID_BAR_TICK_LOC = get_args(_TYPE_BAR.__annotations__["tick_loc"])
|
|
173
171
|
_VALID_BAR_MINOR_TYPE = get_args(_TYPE_BAR.__annotations__["minor_type"])
|
174
172
|
|
175
173
|
_VALIDATE_BAR = {
|
176
|
-
"projection":{"func":vf._validate_projection}, # must be a valid CRS
|
174
|
+
"projection":{"func":vf._validate_projection, "kwargs":{"none_ok":False}}, # must be a valid CRS
|
177
175
|
"unit":{"func":vf._validate_list, "kwargs":{"list":list(units_standard.keys()), "none_ok":True}}, # any of the listed unit values are accepted
|
178
176
|
"rotation":{"func":vf._validate_range, "kwargs":{"min":-360, "max":360, "none_ok":True}}, # between -360 and 360 degrees
|
179
177
|
"max":{"func":vf._validate_range, "kwargs":{"min":0, "max":None, "none_ok":True}}, # between 0 and inf
|