matplotlib-map-utils 2.0.1__py3-none-any.whl → 2.1.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/core/scale_bar.py +3 -3
- matplotlib_map_utils/utils/__init__.py +3 -0
- matplotlib_map_utils/utils/usa.json +1038 -0
- matplotlib_map_utils/utils/usa.py +336 -0
- {matplotlib_map_utils-2.0.1.dist-info → matplotlib_map_utils-2.1.0.dist-info}/METADATA +59 -8
- {matplotlib_map_utils-2.0.1.dist-info → matplotlib_map_utils-2.1.0.dist-info}/RECORD +9 -6
- {matplotlib_map_utils-2.0.1.dist-info → matplotlib_map_utils-2.1.0.dist-info}/WHEEL +1 -1
- {matplotlib_map_utils-2.0.1.dist-info → matplotlib_map_utils-2.1.0.dist-info/licenses}/LICENSE +0 -0
- {matplotlib_map_utils-2.0.1.dist-info → matplotlib_map_utils-2.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,336 @@
|
|
1
|
+
import re
|
2
|
+
import json
|
3
|
+
import warnings
|
4
|
+
from importlib import resources
|
5
|
+
from typing import List, Literal, Union
|
6
|
+
|
7
|
+
# Literal lists, for intellisense
|
8
|
+
regions = Literal["Midwest", "Northeast", "South", "West",
|
9
|
+
"Inhabited Territory", "Uninhabited Territory", "Sovereign State"]
|
10
|
+
|
11
|
+
divisions = Literal["East North Central", "East South Central", "Mid-Atlantic", "Mountain",
|
12
|
+
"New England", "Pacific", "South Atlantic", "West North Central", "West South Central",
|
13
|
+
"Commonwealth", "Compact of Free Association", "Incorporated and Unorganized",
|
14
|
+
"Unincorporated and Unorganized", "Unincorporated and Organized"]
|
15
|
+
|
16
|
+
ombs = Literal["Region I", "Region II", "Region III", "Region IV", "Region IX", "Region V",
|
17
|
+
"Region VI", "Region VII", "Region VIII", "Region X",
|
18
|
+
"Inhabited Territory", "Uninhabited Territory", "Sovereign State"]
|
19
|
+
|
20
|
+
beas = Literal["Far West", "Great Lakes", "Mideast", "New England", "Plains",
|
21
|
+
"Rocky Mountain", "Southeast", "Southwest",
|
22
|
+
"Inhabited Territory", "Uninhabited Territory", "Sovereign State"]
|
23
|
+
|
24
|
+
returns = Literal["fips","name","abbr","object","dict"]
|
25
|
+
|
26
|
+
class USA:
|
27
|
+
# No arguments need to pass on initialization really
|
28
|
+
def __init__(self):
|
29
|
+
self._jurisdictions = self._load_json()
|
30
|
+
|
31
|
+
# This is just for loading the JSON
|
32
|
+
def _load_json(self):
|
33
|
+
with resources.files("matplotlib_map_utils.utils").joinpath("usa.json").open("r") as f:
|
34
|
+
usa_json = json.load(f)
|
35
|
+
return usa_json
|
36
|
+
|
37
|
+
# Getter for all jurisdictions, VALID OR NOT
|
38
|
+
@property
|
39
|
+
def _all(self):
|
40
|
+
return self._jurisdictions
|
41
|
+
|
42
|
+
# Getter for all valid jurisdictions
|
43
|
+
@property
|
44
|
+
def jurisdictions(self):
|
45
|
+
return self.filter_valid(True, self._all, "object")
|
46
|
+
|
47
|
+
# Getter for all valid states
|
48
|
+
@property
|
49
|
+
def states(self):
|
50
|
+
return self.filter_state(True, self.jurisdictions, "object")
|
51
|
+
|
52
|
+
# Getter for all valid territories
|
53
|
+
@property
|
54
|
+
def territories(self):
|
55
|
+
return self.filter_territory(True, self.jurisdictions, "object")
|
56
|
+
|
57
|
+
# Getters to generate distinct values for Region, Division, OMB, and BEA
|
58
|
+
# which are useful if you can't recall which options are valid
|
59
|
+
# First, the function that will get the distinct values
|
60
|
+
def _distinct_options(self, key):
|
61
|
+
# First getting all the available options from the list
|
62
|
+
options = [j[key] for j in self.jurisdictions if j[key] is not None]
|
63
|
+
# Creating the distinct set
|
64
|
+
options_set = set(options)
|
65
|
+
# Returning the set (but as a list)
|
66
|
+
# this will also be alphabetically sorted
|
67
|
+
options = list(options_set)
|
68
|
+
options.sort()
|
69
|
+
return options
|
70
|
+
|
71
|
+
# The getters are now just calls to the properties
|
72
|
+
@property
|
73
|
+
def regions(self):
|
74
|
+
return self._distinct_options("region")
|
75
|
+
|
76
|
+
@property
|
77
|
+
def divisions(self):
|
78
|
+
return self._distinct_options("division")
|
79
|
+
|
80
|
+
@property
|
81
|
+
def omb(self):
|
82
|
+
return self._distinct_options("omb")
|
83
|
+
|
84
|
+
@property
|
85
|
+
def bea(self):
|
86
|
+
return self._distinct_options("bea")
|
87
|
+
|
88
|
+
# Main filter function
|
89
|
+
# Each filter step will follow the same process
|
90
|
+
## Check that there is a non-None filter
|
91
|
+
## Normalize the input to be in a list (if not already)
|
92
|
+
## Perform the filter step
|
93
|
+
# Each step is also available as its own independent function, as needed
|
94
|
+
def filter(self, valid: bool | None=True,
|
95
|
+
fips: str | int | None=None,
|
96
|
+
name: str | None=None,
|
97
|
+
abbr: str | None=None,
|
98
|
+
state: bool | None=None,
|
99
|
+
contiguous: bool | None=None,
|
100
|
+
territory: bool | None=None,
|
101
|
+
region: Union[regions, List[regions]]=None,
|
102
|
+
division: Union[divisions, List[divisions]]=None,
|
103
|
+
omb: Union[ombs, List[ombs]]=None,
|
104
|
+
bea: Union[beas, List[beas]]=None,
|
105
|
+
to_return: Union[returns, List[returns]]="fips"):
|
106
|
+
|
107
|
+
# Getting a copy of our jurisdictions, which will be filtered each time
|
108
|
+
filter_juris = self.jurisdictions.copy()
|
109
|
+
|
110
|
+
# Starting with an initial valid filtering
|
111
|
+
# Which will drop invalid FIPS codes 03, 07, 14, 43, and 52
|
112
|
+
if (valid is not None) and (len(filter_juris) > 0):
|
113
|
+
filter_juris = self.filter_valid(valid, filter_juris, to_return="_ignore")
|
114
|
+
|
115
|
+
# Going through each step
|
116
|
+
if (fips is not None) and (len(filter_juris) > 0):
|
117
|
+
filter_juris = self.filter_fips(fips, filter_juris, to_return="_ignore")
|
118
|
+
|
119
|
+
if (name is not None) and (len(filter_juris) > 0):
|
120
|
+
filter_juris = self.filter_name(name, filter_juris, to_return="_ignore")
|
121
|
+
|
122
|
+
if (abbr is not None) and (len(filter_juris) > 0):
|
123
|
+
filter_juris = self.filter_abbr(abbr, filter_juris, to_return="_ignore")
|
124
|
+
|
125
|
+
if (state is not None) and (len(filter_juris) > 0):
|
126
|
+
filter_juris = self.filter_state(state, filter_juris, to_return="_ignore")
|
127
|
+
|
128
|
+
if (contiguous is not None) and (len(filter_juris) > 0):
|
129
|
+
filter_juris = self.filter_contiguous(contiguous, filter_juris, to_return="_ignore")
|
130
|
+
|
131
|
+
if (territory is not None) and (len(filter_juris) > 0):
|
132
|
+
filter_juris = self.filter_territory(territory, filter_juris, to_return="_ignore")
|
133
|
+
|
134
|
+
if (region is not None) and (len(filter_juris) > 0):
|
135
|
+
filter_juris = self.filter_region(region, filter_juris, to_return="_ignore")
|
136
|
+
|
137
|
+
if (division is not None) and (len(filter_juris) > 0):
|
138
|
+
filter_juris = self.filter_division(division, filter_juris, to_return="_ignore")
|
139
|
+
|
140
|
+
if (omb is not None) and (len(filter_juris) > 0):
|
141
|
+
filter_juris = self.filter_omb(omb, filter_juris, to_return="_ignore")
|
142
|
+
|
143
|
+
if (bea is not None) and (len(filter_juris) > 0):
|
144
|
+
filter_juris = self.filter_bea(bea, filter_juris, to_return="_ignore")
|
145
|
+
|
146
|
+
# Final step is to process the input based on to_return
|
147
|
+
# and then return it!
|
148
|
+
return self._process_return(filter_juris, to_return)
|
149
|
+
|
150
|
+
# Filtering bool values (valid, state, contiguous, territory)
|
151
|
+
# Will accept either true or false
|
152
|
+
def _filter_bool(self, value, key, to_filter=None, to_return="_ignore"):
|
153
|
+
# If nothing is passed to to_filter, getting the jurisdictions list
|
154
|
+
to_filter = self.jurisdictions.copy() if to_filter is None else to_filter
|
155
|
+
if not isinstance(value, bool):
|
156
|
+
warnings.warn(f"Invalid {key} filter: {value}. Only boolean values (True/False) are considered valid, see documentation for details.")
|
157
|
+
else:
|
158
|
+
# Performing the filter
|
159
|
+
filtered = [j for j in to_filter if j[key] == value]
|
160
|
+
# And returning the values
|
161
|
+
return self._process_return(filtered, to_return)
|
162
|
+
|
163
|
+
# Shortcuts for filtering based on valid, state, contiguous, and territory
|
164
|
+
def filter_valid(self, valid: bool, to_filter=None, to_return="fips"):
|
165
|
+
return self._filter_bool(valid, "valid", to_filter, to_return)
|
166
|
+
|
167
|
+
def filter_state(self, state: bool, to_filter=None, to_return="fips"):
|
168
|
+
return self._filter_bool(state, "state", to_filter, to_return)
|
169
|
+
|
170
|
+
def filter_contiguous(self, contiguous: bool, to_filter=None, to_return="fips"):
|
171
|
+
return self._filter_bool(contiguous, "contiguous", to_filter, to_return)
|
172
|
+
|
173
|
+
def filter_territory(self, territory: bool, to_filter=None, to_return="fips"):
|
174
|
+
return self._filter_bool(territory, "territory", to_filter, to_return)
|
175
|
+
|
176
|
+
# Filtering FIPS
|
177
|
+
# Will accept an integer or a two-digit string as an input
|
178
|
+
# If a longer string is inserted, will truncate to only the first two characters
|
179
|
+
def filter_fips(self, fips: str | List[str], to_filter=None, to_return="abbr"):
|
180
|
+
# If nothing is passed to to_filter, getting the jurisdictions list
|
181
|
+
to_filter = self.jurisdictions.copy() if to_filter is None else to_filter
|
182
|
+
# Normalizing the fips value being passed
|
183
|
+
fips = self._normalize_input(fips)
|
184
|
+
# This will store the cleaned-up fips codes
|
185
|
+
fips_clean = []
|
186
|
+
for f in fips:
|
187
|
+
# If the input is an integer, convert it to a two-digit string
|
188
|
+
if isinstance(f, int):
|
189
|
+
fips_clean.append(str(f).zfill(2)[:2])
|
190
|
+
# If the input is already a string, get the first two characters
|
191
|
+
elif isinstance(f, str):
|
192
|
+
fips_clean.append(f.zfill(2)[:2])
|
193
|
+
# Otherwise, throw a *warning*
|
194
|
+
else:
|
195
|
+
warnings.warn(f"Invalid FIPS filter: {f}. Only integers and strings are considered valid, see documentation for details.")
|
196
|
+
# Now can use the clean fips to actually filter
|
197
|
+
filtered = [j for j in to_filter if j["fips"] in fips_clean]
|
198
|
+
# And returning the values
|
199
|
+
return self._process_return(filtered, to_return)
|
200
|
+
|
201
|
+
# Filtering name
|
202
|
+
# Will accept strings
|
203
|
+
# Will normalize the string first (trim, case, special characters), before checking
|
204
|
+
# Some states also have an alias available for checking against (Washington, D.C. and District of Columbia are equivalent)
|
205
|
+
def filter_name(self, name: str | List[str], to_filter=None, to_return="fips"):
|
206
|
+
# If nothing is passed to to_filter, getting the jurisdictions list
|
207
|
+
to_filter = self.jurisdictions.copy() if to_filter is None else to_filter
|
208
|
+
# Normalizing the name input being passed
|
209
|
+
name = self._normalize_input(name)
|
210
|
+
# This will store the cleaned-up name input
|
211
|
+
name_clean = []
|
212
|
+
for n in name:
|
213
|
+
# If the input is a string, clean it
|
214
|
+
if isinstance(n, str):
|
215
|
+
name_clean.append(self._normalize_string(n, case="lower"))
|
216
|
+
else:
|
217
|
+
warnings.warn(f"Invalid name filter: {n}. Only strings are considered valid, see documentation for details.")
|
218
|
+
# Now we can use the clean name to filter
|
219
|
+
# Note that we also normalize the names and aliases in our to_filter list!
|
220
|
+
filtered = [j for j in to_filter if ((self._normalize_string(j["name"], case="lower") in name_clean) or
|
221
|
+
(j["alias"] is not None and self._normalize_string(j["alias"], case="lower") in name_clean))]
|
222
|
+
# And returning the values
|
223
|
+
return self._process_return(filtered, to_return)
|
224
|
+
|
225
|
+
# Filtering abbr
|
226
|
+
# Will accept strings
|
227
|
+
# Will normalize the string first (trim, case, special characters), before checking
|
228
|
+
# If a string longer than two characters is passed, will only look at the first two characters!
|
229
|
+
def filter_abbr(self, abbr: str | List[str], to_filter=None, to_return="fips"):
|
230
|
+
# If nothing is passed to to_filter, getting the jurisdictions list
|
231
|
+
to_filter = self.jurisdictions.copy() if to_filter is None else to_filter
|
232
|
+
# Normalizing the input being passed
|
233
|
+
abbr = self._normalize_input(abbr)
|
234
|
+
# This will store the cleaned-up input
|
235
|
+
abbr_clean = []
|
236
|
+
for a in abbr:
|
237
|
+
# If the input is a string, clean it
|
238
|
+
if isinstance(a, str):
|
239
|
+
abbr_clean.append(self._normalize_string(a, case="lower"))
|
240
|
+
else:
|
241
|
+
warnings.warn(f"Invalid abbr filter: {a}. Only strings are considered valid, see documentation for details.")
|
242
|
+
# Now we can use the clean input to filter
|
243
|
+
filtered = [j for j in to_filter if (j["abbr"] is not None and self._normalize_string(j["abbr"], case="lower")[:2] in abbr_clean)]
|
244
|
+
# And returning the values
|
245
|
+
return self._process_return(filtered, to_return)
|
246
|
+
|
247
|
+
# Filtering for categorical values (region/division/omb/bea)
|
248
|
+
# Will get the list of acceptable values and compare inputs to it
|
249
|
+
# while also warning if an invalid filter is requested
|
250
|
+
def _filter_categorical(self, input, key, to_filter=None, to_return="_ignore"):
|
251
|
+
# If nothing is passed to to_filter, getting the jurisdictions list
|
252
|
+
to_filter = self.jurisdictions.copy() if to_filter is None else to_filter
|
253
|
+
# Normalizing the input being passed
|
254
|
+
input = self._normalize_input(input)
|
255
|
+
# This has the acceptable inputs we want to compare against
|
256
|
+
accepted_inputs = self._distinct_options(key)
|
257
|
+
# This will store the cleaned-up input
|
258
|
+
input_clean = []
|
259
|
+
for i in input:
|
260
|
+
# If the input is not a string, warn
|
261
|
+
if not isinstance(i, str):
|
262
|
+
warnings.warn(f"Invalid {key} filter: {i}. Only strings are considered valid, see documentation for details.")
|
263
|
+
# If the input is not in our list, warn the user
|
264
|
+
elif i not in accepted_inputs:
|
265
|
+
warnings.warn(f"Invalid {key} filter: {i}. Only the following inputs are considered valid: {accepted_inputs}.")
|
266
|
+
# Otherwise, add it to our list
|
267
|
+
else:
|
268
|
+
input_clean.append(i)
|
269
|
+
# Now we can use the clean input to filter
|
270
|
+
filtered = [j for j in to_filter if j[key] in input_clean]
|
271
|
+
# And returning the values
|
272
|
+
return self._process_return(filtered, to_return)
|
273
|
+
|
274
|
+
# Iterations for each categorical filter based on their respective inputs
|
275
|
+
def filter_region(self, region: Union[regions, List[regions]], to_filter=None, to_return="fips"):
|
276
|
+
return self._filter_categorical(region, "region", to_filter, to_return)
|
277
|
+
|
278
|
+
def filter_division(self, division: Union[divisions, List[divisions]], to_filter=None, to_return="fips"):
|
279
|
+
return self._filter_categorical(division, "division", to_filter, to_return)
|
280
|
+
|
281
|
+
def filter_omb(self, omb: Union[ombs, List[ombs]], to_filter=None, to_return="fips"):
|
282
|
+
return self._filter_categorical(omb, "omb", to_filter, to_return)
|
283
|
+
|
284
|
+
def filter_bea(self, bea: Union[beas, List[beas]], to_filter=None, to_return="fips"):
|
285
|
+
return self._filter_categorical(bea, "bea", to_filter, to_return)
|
286
|
+
|
287
|
+
# Function that processes the returning of a filtered jurisdiction
|
288
|
+
def _process_return(self, filter_juris, to_return):
|
289
|
+
# If the length is zero, warn!
|
290
|
+
if filter_juris is None or len(filter_juris) == 0:
|
291
|
+
warnings.warn(f"No matching entities found. Please refer to the documentation and double-check your filters.")
|
292
|
+
return None
|
293
|
+
if to_return is None:
|
294
|
+
to_return == "_ignore"
|
295
|
+
# Available options for to_return include fips, name, and abbr
|
296
|
+
elif to_return.lower() == "fips":
|
297
|
+
juris_return = [j["fips"] for j in filter_juris]
|
298
|
+
elif to_return.lower() == "name":
|
299
|
+
juris_return = [j["name"] for j in filter_juris]
|
300
|
+
elif to_return.lower() == "abbr":
|
301
|
+
juris_return = [j["abbr"] for j in filter_juris]
|
302
|
+
# Can also request that the entire object be returned, in which case nothing is done
|
303
|
+
# This will also happen if an invalid return object is passed
|
304
|
+
elif to_return.lower() not in ["object","dict","_ignore"]:
|
305
|
+
warnings.warn(f"Invalid to_return request: {to_return}. The entire object will be returned.")
|
306
|
+
juris_return = filter_juris.copy()
|
307
|
+
else:
|
308
|
+
juris_return = filter_juris.copy()
|
309
|
+
|
310
|
+
# Now, also processing the return request based on the length of the returned list
|
311
|
+
# If the length is zero, warn!
|
312
|
+
if len(juris_return) == 0:
|
313
|
+
warnings.warn(f"No matching entities found. Please refer to the documentation and double-check your filters.")
|
314
|
+
return None
|
315
|
+
# If only one element is returned, return the element itself, not a list
|
316
|
+
elif len(juris_return) == 1 and to_return != "_ignore":
|
317
|
+
return juris_return[0]
|
318
|
+
# Otherwise return the whole thing
|
319
|
+
else:
|
320
|
+
return juris_return
|
321
|
+
|
322
|
+
# Utility function to normalize a string that is passed to it
|
323
|
+
def _normalize_string(self, string, case="keep", nan="", spaces="_"):
|
324
|
+
string = string.strip()
|
325
|
+
if case == "lower":
|
326
|
+
string = string.lower()
|
327
|
+
string = re.sub(r"\W\S",nan,string)
|
328
|
+
string = re.sub(r"\s",spaces,string)
|
329
|
+
return string
|
330
|
+
|
331
|
+
# Utility function to convert a relevant non-list input to a list
|
332
|
+
def _normalize_input(self, input):
|
333
|
+
if not isinstance(input, (list, tuple)):
|
334
|
+
return [input]
|
335
|
+
else:
|
336
|
+
return input
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: matplotlib-map-utils
|
3
|
-
Version: 2.0
|
3
|
+
Version: 2.1.0
|
4
4
|
Summary: A suite of tools for creating maps in matplotlib
|
5
5
|
Author-email: David Moss <davidmoss1221@gmail.com>
|
6
6
|
Project-URL: Homepage, https://github.com/moss-xyz/matplotlib-map-utils/
|
@@ -8,12 +8,13 @@ Project-URL: Bug Tracker, https://github.com/moss-xyz/matplotlib-map-utils/issue
|
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
9
9
|
Classifier: License :: OSI Approved :: GNU General Public License (GPL)
|
10
10
|
Classifier: Operating System :: OS Independent
|
11
|
-
Requires-Python: >=3.
|
11
|
+
Requires-Python: >=3.10
|
12
12
|
Description-Content-Type: text/markdown
|
13
13
|
License-File: LICENSE
|
14
14
|
Requires-Dist: matplotlib>=3.9.0
|
15
15
|
Requires-Dist: cartopy>=0.23.0
|
16
16
|
Requires-Dist: great-circle-calculator>=1.3.1
|
17
|
+
Dynamic: license-file
|
17
18
|
|
18
19
|
# matplotlib-map-utils
|
19
20
|
|
@@ -29,12 +30,14 @@ Requires-Dist: great-circle-calculator>=1.3.1
|
|
29
30
|
|
30
31
|
`matplotlib_map_utils` is intended to be a package that provides various functions and objects that assist with the the creation of maps using [`matplotlib`](https://matplotlib.org/stable/).
|
31
32
|
|
32
|
-
As of `v2.x` (the current version), this includes two tools:
|
33
|
+
As of `v2.x` (the current version), this includes two tools and one utility:
|
33
34
|
|
34
35
|
* `north_arrow.py`, which generates a high quality, context-aware north arrow for a given plot.
|
35
36
|
|
36
37
|
* `scale_bar.py`, which generates a high quality, context-aware scale bar to a given plot.
|
37
38
|
|
39
|
+
* `usa.py`, which contains a class that helps filter for states and territories within the USA based on given characteristics.
|
40
|
+
|
38
41
|
Future releases (if the project is continued) might provide a similar tool inset maps, or other functions that I have created myself that give more control in the formatting of maps.
|
39
42
|
|
40
43
|
---
|
@@ -49,11 +52,11 @@ pip install matplotlib-map-utils
|
|
49
52
|
|
50
53
|
The requirements for this package are:
|
51
54
|
|
52
|
-
* `python >= 3.
|
55
|
+
* `python >= 3.10` (due to the use of the pipe operator to concatenate dictionaries and types)
|
53
56
|
|
54
|
-
* `matplotlib >= 3.9
|
57
|
+
* `matplotlib >= 3.9` (might work with lower versions but not guaranteed)
|
55
58
|
|
56
|
-
* `cartopy >= 0.23
|
59
|
+
* `cartopy >= 0.23` (due to earlier bug with calling `copy()` on `CRS` objects)
|
57
60
|
|
58
61
|
---
|
59
62
|
|
@@ -78,6 +81,10 @@ package_name/
|
|
78
81
|
│ ├── __init__.py
|
79
82
|
│ ├── north_arrow.py
|
80
83
|
│ └── scale_bar.py
|
84
|
+
├── utils/
|
85
|
+
│ ├── __init__.py
|
86
|
+
│ ├── usa.py
|
87
|
+
│ └── usa.json
|
81
88
|
```
|
82
89
|
|
83
90
|
Where:
|
@@ -230,6 +237,38 @@ Refer to `docs\howto_scale_bar` for details on how to customize each facet of th
|
|
230
237
|
|
231
238
|
---
|
232
239
|
|
240
|
+
### Utilities
|
241
|
+
|
242
|
+
<details>
|
243
|
+
<summary><i>Expand instructions</i></summary>
|
244
|
+
|
245
|
+
#### Quick Start
|
246
|
+
|
247
|
+
Importing the bundled utility functions and classes can be done like so:
|
248
|
+
|
249
|
+
```py
|
250
|
+
from matplotlib_map_utils.utils import USA
|
251
|
+
```
|
252
|
+
|
253
|
+
As of `v2.1.0`, there is only one utility class available: `USA`, an object to help quickly filter for subsets of US states and territories. This utility class is still in beta, and might change.
|
254
|
+
|
255
|
+
An example:
|
256
|
+
|
257
|
+
```python
|
258
|
+
# Loading the object
|
259
|
+
usa = USA()
|
260
|
+
# Getting a list FIPS codes for US States
|
261
|
+
usa.filter(states=True, to_return="fips")
|
262
|
+
# Getting a list of State Names for states in the South and Midwest regions
|
263
|
+
usa.filter(region=["South","Midtwest"], to_return="name")
|
264
|
+
```
|
265
|
+
|
266
|
+
Refer to `docs\howto_utils` for details on how to use this class, including with `pandas.apply()`.
|
267
|
+
|
268
|
+
</details>
|
269
|
+
|
270
|
+
---
|
271
|
+
|
233
272
|
### Development Notes
|
234
273
|
|
235
274
|
#### Inspiration and Thanks
|
@@ -246,6 +285,10 @@ Two more projects assisted with the creation of this script:
|
|
246
285
|
|
247
286
|
- `v2.0.1`: Fixed a bug in the `dual_bars()` function that prevented empty dictionaries to be passed. Also added a warning when auto-calculated bar widths appear to be exceeding the dimension of the axis (usually occurs when the axis is <2 kilometeres or miles long, depending on the units selected).
|
248
287
|
|
288
|
+
- `v2.0.2`: Changed f-string formatting to alternate double and single quotes, so as to maintain compatibility with versions of Python before 3.12 (see [here](https://github.com/moss-xyz/matplotlib-map-utils/issues/3)). However, this did reveal that another aspect of the code, namely concatenating `type` in function arguments, requires 3.10, and so the minimum python version was incremented.
|
289
|
+
|
290
|
+
- `v2.1.0`: Added a utility class, `USA`, for filtering subsets of US states and territories based on FIPS code, name, abbreviation, region, division, and more. This is considered a beta release, and might be subject to change later on.
|
291
|
+
|
249
292
|
#### Future Roadmap
|
250
293
|
|
251
294
|
With the release of `v2.x`, and the addition of **Scale Bar** tools, this project has achieved the two main objectives that I set out to.
|
@@ -268,7 +311,15 @@ If I continue development of this project, I will be looking to add or fix the f
|
|
268
311
|
|
269
312
|
* Create more styles for the bar, potentiallly including dual boxes and a sawtooth bar
|
270
313
|
|
271
|
-
|
314
|
+
* **Utils:**
|
315
|
+
|
316
|
+
* (USA): Stronger fuzzy search mechanics, so that it will accept flexible inputs for FIPS/abbr/name
|
317
|
+
|
318
|
+
* (USA): More integrated class types to allow for a more fully-formed object model (USA being a `Country`, with subclasses related to `State` and `Territory` that have their own classes of attributes, etc.)
|
319
|
+
|
320
|
+
* (USA): Stronger typing options, so you don't have to recall which `region` or `division` types are available, etc.
|
321
|
+
|
322
|
+
If that goes well, `v3` can then either create a tool for generating inset maps (which `matplotlib` has *some* support for), or the various functions that I have created in the past that assist with formatting a map "properly", such as centering on a given object.
|
272
323
|
|
273
324
|
I am also open to ideas for other extensions to create!
|
274
325
|
|
@@ -1,18 +1,21 @@
|
|
1
1
|
matplotlib_map_utils/__init__.py,sha256=X6lN1yfP4ckb6l-ej20YqFvIHUNLZQxRWnvvyWBlJEY,305
|
2
2
|
matplotlib_map_utils/core/__init__.py,sha256=G4fxPpfE77EhZr7yGZCjppP7zvwRthl8yHM0b2KgrFs,184
|
3
3
|
matplotlib_map_utils/core/north_arrow.py,sha256=vikwYtSP2-sPRF_SQBALezB3uEY_PHA9dglm503hkvU,22531
|
4
|
-
matplotlib_map_utils/core/scale_bar.py,sha256=
|
4
|
+
matplotlib_map_utils/core/scale_bar.py,sha256=l2tS35xa_pv40lag4Y00hT6FEGhQ-wOb07ipxDUYCNw,62248
|
5
5
|
matplotlib_map_utils/defaults/__init__.py,sha256=_pegE5kv_sb0ansSF4XpWBRwboaP4zUjWY1KIGbK-TE,119
|
6
6
|
matplotlib_map_utils/defaults/north_arrow.py,sha256=uZb1RsUWxFTHywm8HATj_9iPF_GjCs_Z2HOn0JchjTY,8571
|
7
7
|
matplotlib_map_utils/defaults/scale_bar.py,sha256=GpXiWUHcOsv43G1HOfpqw-dzDPQQzQB7RNdtIf0e7Bc,8225
|
8
8
|
matplotlib_map_utils/scratch/map_utils.py,sha256=j8dOX9uuotl9rRCAXapFLHycUwVE4nzIrqWYOGG2Lgg,19653
|
9
9
|
matplotlib_map_utils/scratch/north_arrow_old_classes.py,sha256=1xKQ6yUghX4BWzIv8GsGBHDDPJ8B0Na7ixdw2jgtTqw,50993
|
10
|
+
matplotlib_map_utils/utils/__init__.py,sha256=uUy0kUMMGrDpvo88J_OLk2dQI-UwCXclccaEyk8x5R0,41
|
11
|
+
matplotlib_map_utils/utils/usa.json,sha256=kLB9JXNSWf8VU-9XwXuMRAPKO-zA4aluQUEln7Ktc_s,26563
|
12
|
+
matplotlib_map_utils/utils/usa.py,sha256=7SlUdxtCan5PFNIoLe-HfOC5r2cxJAF-9QKhNIK71EI,16853
|
10
13
|
matplotlib_map_utils/validation/__init__.py,sha256=0fL3N63jxjRwTU44b7-6ZYZJfOT_0ac7dx7M6Gpu_5M,52
|
11
14
|
matplotlib_map_utils/validation/functions.py,sha256=QpOHs-GQ1NUMXO0HxAtEZvAcrXwsIE2ekqUhYm-IKGg,11783
|
12
15
|
matplotlib_map_utils/validation/north_arrow.py,sha256=dlWbcKit7dq93PJVrv1efE_865irT6zwBuqD6NYLYPg,10349
|
13
16
|
matplotlib_map_utils/validation/scale_bar.py,sha256=7rYs7ei0rQ5iJfapcBWkn7s4P-CnSh9B441GpsGpFO4,17628
|
14
|
-
matplotlib_map_utils-2.0.
|
15
|
-
matplotlib_map_utils-2.0.
|
16
|
-
matplotlib_map_utils-2.0.
|
17
|
-
matplotlib_map_utils-2.0.
|
18
|
-
matplotlib_map_utils-2.0.
|
17
|
+
matplotlib_map_utils-2.1.0.dist-info/licenses/LICENSE,sha256=aFLFZg6LEJFpTlNQ8su3__jw4GfV-xWBmC1cePkKZVw,35802
|
18
|
+
matplotlib_map_utils-2.1.0.dist-info/METADATA,sha256=KaD3ZBhrkgi25DYJV2xJXx0wjcTM9MgmNcNr58Z6UcE,12839
|
19
|
+
matplotlib_map_utils-2.1.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
20
|
+
matplotlib_map_utils-2.1.0.dist-info/top_level.txt,sha256=6UyDpxsnMhSOd9a-abQe0lLJveybJyYtUHMdX7zXgKA,21
|
21
|
+
matplotlib_map_utils-2.1.0.dist-info/RECORD,,
|
{matplotlib_map_utils-2.0.1.dist-info → matplotlib_map_utils-2.1.0.dist-info/licenses}/LICENSE
RENAMED
File without changes
|
File without changes
|