-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Hatchmap and scattermap fixes #195
base: main
Are you sure you want to change the base?
Changes from 14 commits
7f4f706
59dc895
16178c6
ab15418
cb91ead
336c1c9
bf22010
284441b
2bc3a23
5d28a54
12cb82e
77acc52
3bc5bca
8217744
b47c53f
ede8a63
0a99562
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -1526,17 +1526,15 @@ def scattermap( | |||||||||||||
if "row" not in plot_kw and "col" not in plot_kw: | ||||||||||||||
use_attrs.setdefault("title", "description") | ||||||||||||||
|
||||||||||||||
plot_kw_pop = copy.deepcopy(plot_kw) # copy plot_kw to modify and pop info in it | ||||||||||||||
|
||||||||||||||
# extract plot_kw from dict if needed | ||||||||||||||
if isinstance(data, dict) and plot_kw and list(data.keys())[0] in plot_kw.keys(): | ||||||||||||||
plot_kw_pop = plot_kw_pop[list(data.keys())[0]] | ||||||||||||||
plot_kw = plot_kw[list(data.keys())[0]] | ||||||||||||||
|
||||||||||||||
# figanos does not use xr.plot.scatter default markersize | ||||||||||||||
if "markersize" in plot_kw.keys(): | ||||||||||||||
if not sizes: | ||||||||||||||
sizes = plot_kw["markersize"] | ||||||||||||||
plot_kw_pop.pop("markersize") | ||||||||||||||
plot_kw.pop("markersize") | ||||||||||||||
|
||||||||||||||
# if data is dict, extract | ||||||||||||||
if isinstance(data, dict): | ||||||||||||||
|
@@ -1576,13 +1574,13 @@ def scattermap( | |||||||||||||
elif ax is not None and ("col" in plot_kw or "row" in plot_kw): | ||||||||||||||
raise ValueError("Cannot use 'ax' and 'col'/'row' at the same time.") | ||||||||||||||
elif ax is None: | ||||||||||||||
plot_kw_pop = {"subplot_kws": {"projection": projection}} | plot_kw_pop | ||||||||||||||
plot_kw = {"subplot_kws": {"projection": projection}} | plot_kw | ||||||||||||||
cfig_kw = fig_kw.copy() | ||||||||||||||
if "figsize" in fig_kw: # add figsize to plot_kw for facetgrid | ||||||||||||||
plot_kw_pop.setdefault("figsize", fig_kw["figsize"]) | ||||||||||||||
plot_kw.setdefault("figsize", fig_kw["figsize"]) | ||||||||||||||
cfig_kw.pop("figsize") | ||||||||||||||
if len(cfig_kw) >= 1: | ||||||||||||||
plot_kw_pop = {"subplot_kws": {"projection": projection}} | plot_kw_pop | ||||||||||||||
plot_kw = {"subplot_kws": {"projection": projection}} | plot_kw | ||||||||||||||
warnings.warn( | ||||||||||||||
"Only figsize and figure.add_subplot() arguments can be passed to fig_kw when using facetgrid." | ||||||||||||||
) | ||||||||||||||
|
@@ -1602,9 +1600,9 @@ def scattermap( | |||||||||||||
cbar_label = get_attributes(use_attrs["cbar_label"], data) | ||||||||||||||
|
||||||||||||||
if "add_colorbar" not in plot_kw or plot_kw["add_colorbar"] is not False: | ||||||||||||||
plot_kw_pop.setdefault("cbar_kwargs", {}) | ||||||||||||||
plot_kw_pop["cbar_kwargs"].setdefault("label", wrap_text(cbar_label)) | ||||||||||||||
plot_kw_pop["cbar_kwargs"].setdefault("pad", 0.015) | ||||||||||||||
plot_kw.setdefault("cbar_kwargs", {}) | ||||||||||||||
plot_kw["cbar_kwargs"].setdefault("label", wrap_text(cbar_label)) | ||||||||||||||
plot_kw["cbar_kwargs"].setdefault("pad", 0.015) | ||||||||||||||
|
||||||||||||||
# colormap | ||||||||||||||
if isinstance(cmap, str): | ||||||||||||||
|
@@ -1656,61 +1654,68 @@ def scattermap( | |||||||||||||
target_range=size_range, | ||||||||||||||
data_range=None, | ||||||||||||||
) | ||||||||||||||
plot_kw_pop.setdefault("add_legend", False) | ||||||||||||||
plot_kw.setdefault("add_legend", False) | ||||||||||||||
if ax: | ||||||||||||||
plot_kw_pop.setdefault("s", pt_sizes) | ||||||||||||||
plot_kw.setdefault("s", pt_sizes) | ||||||||||||||
else: | ||||||||||||||
plot_kw_pop.setdefault("s", pt_sizes[0]) | ||||||||||||||
plot_kw.setdefault("s", pt_sizes[0]) | ||||||||||||||
|
||||||||||||||
# norm | ||||||||||||||
plot_kw_pop.setdefault("vmin", np.nanmin(plot_data.values[mask])) | ||||||||||||||
plot_kw_pop.setdefault("vmax", np.nanmax(plot_data.values[mask])) | ||||||||||||||
plot_kw.setdefault("vmin", np.nanmin(plot_data.values[mask])) | ||||||||||||||
plot_kw.setdefault("vmax", np.nanmax(plot_data.values[mask])) | ||||||||||||||
|
||||||||||||||
norm = custom_cmap_norm( | ||||||||||||||
cmap, | ||||||||||||||
vmin=plot_kw_pop["vmin"], | ||||||||||||||
vmax=plot_kw_pop["vmax"], | ||||||||||||||
vmin=plot_kw["vmin"], | ||||||||||||||
vmax=plot_kw["vmax"], | ||||||||||||||
levels=levels, | ||||||||||||||
divergent=divergent, | ||||||||||||||
) | ||||||||||||||
|
||||||||||||||
# matplotlib.pyplot.scatter treats "edgecolor" and "edgecolors" as aliases so we accept "edgecolor" and convert it | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wouldn't pop edgecolor and replace it to edgecolors since both of them work and if matplotlib ever made change to one we would have to fix it after. I also think edgecolors is more used to pass many colors as a list. |
||||||||||||||
if "edgecolor" in plot_kw and "edgecolors" not in plot_kw: | ||||||||||||||
plot_kw["edgecolors"] = plot_kw["edgecolor"] | ||||||||||||||
plot_kw.pop("edgecolor") | ||||||||||||||
|
||||||||||||||
# set defaults and create copy without vmin, vmax (conflicts with norm) | ||||||||||||||
plot_kw_pop = { | ||||||||||||||
plot_kw = { | ||||||||||||||
"cmap": cmap, | ||||||||||||||
"norm": norm, | ||||||||||||||
"transform": transform, | ||||||||||||||
"zorder": 8, | ||||||||||||||
"marker": "o", | ||||||||||||||
} | plot_kw_pop | ||||||||||||||
"edgecolors": "none", | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would remove this line based on previous comment |
||||||||||||||
} | plot_kw | ||||||||||||||
|
||||||||||||||
# chek if edgecolors in plot_kw and match len of plot_data | ||||||||||||||
if "edgecolors" in plot_kw: | ||||||||||||||
if matplotlib.colors.is_color_like(plot_kw["edgecolors"]): | ||||||||||||||
plot_kw_pop["edgecolors"] = np.repeat( | ||||||||||||||
plot_kw["edgecolors"] = np.repeat( | ||||||||||||||
plot_kw["edgecolors"], len(plot_data.where(mask).values) | ||||||||||||||
) | ||||||||||||||
elif len(plot_kw["edgecolors"]) != len(plot_data.values): | ||||||||||||||
plot_kw_pop["edgecolors"] = np.repeat( | ||||||||||||||
plot_kw["edgecolors"] = np.repeat( | ||||||||||||||
plot_kw["edgecolors"][0], len(plot_data.where(mask).values) | ||||||||||||||
) | ||||||||||||||
warnings.warn( | ||||||||||||||
"Length of edgecolors does not match length of data. Only first edgecolor is used for plotting." | ||||||||||||||
) | ||||||||||||||
else: | ||||||||||||||
if isinstance(plot_kw["edgecolors"], list): | ||||||||||||||
plot_kw_pop["edgecolors"] = np.array(plot_kw["edgecolors"]) | ||||||||||||||
plot_kw_pop["edgecolors"] = plot_kw_pop["edgecolors"][mask] | ||||||||||||||
plot_kw["edgecolors"] = np.array(plot_kw["edgecolors"]) | ||||||||||||||
plot_kw["edgecolors"] = plot_kw["edgecolors"][mask] | ||||||||||||||
else: | ||||||||||||||
plot_kw_pop.setdefault("edgecolor", "none") | ||||||||||||||
plot_kw.setdefault("edgecolors", "none") | ||||||||||||||
|
||||||||||||||
for key in ["vmin", "vmax"]: | ||||||||||||||
plot_kw_pop.pop(key) | ||||||||||||||
plot_kw.pop(key) | ||||||||||||||
# plot | ||||||||||||||
plot_kw_pop = {"x": "lon", "y": "lat", "hue": plot_data.name} | plot_kw_pop | ||||||||||||||
plot_kw = {"x": "lon", "y": "lat", "hue": plot_data.name} | plot_kw | ||||||||||||||
if ax: | ||||||||||||||
plot_kw_pop.setdefault("ax", ax) | ||||||||||||||
v = plot_data.where(mask).to_dataset() | ||||||||||||||
im = v.plot.scatter(**plot_kw_pop) | ||||||||||||||
plot_kw.setdefault("ax", ax) | ||||||||||||||
|
||||||||||||||
plot_data_masked = plot_data.where(mask).to_dataset() | ||||||||||||||
im = plot_data_masked.plot.scatter(**plot_kw) | ||||||||||||||
|
||||||||||||||
# add features | ||||||||||||||
if ax: | ||||||||||||||
|
@@ -1773,7 +1778,7 @@ def scattermap( | |||||||||||||
np.resize(sdata.values[mask], (sdata.values[mask].size, 1)), | ||||||||||||||
np.resize(pt_sizes[mask], (pt_sizes[mask].size, 1)), | ||||||||||||||
max_entries=6, | ||||||||||||||
marker=plot_kw_pop["marker"], | ||||||||||||||
marker=plot_kw["marker"], | ||||||||||||||
) | ||||||||||||||
# legend spacing | ||||||||||||||
if size_range[1] > 200: | ||||||||||||||
|
@@ -2077,7 +2082,7 @@ def hatchmap( | |||||||||||||
features: list[str] | dict[str, dict[str, Any]] | None = None, | ||||||||||||||
geometries_kw: dict[str, Any] | None = None, | ||||||||||||||
levels: int | None = None, | ||||||||||||||
legend_kw: dict[str, Any] | None = None, | ||||||||||||||
legend_kw: dict[str, Any] | bool = True, | ||||||||||||||
show_time: bool | str | int | tuple[float, float] = False, | ||||||||||||||
frame: bool = False, | ||||||||||||||
) -> matplotlib.axes.Axes: | ||||||||||||||
|
@@ -2108,8 +2113,8 @@ def hatchmap( | |||||||||||||
cartopy.feature: ['coastline', 'borders', 'lakes', 'land', 'ocean', 'rivers', 'states']. | ||||||||||||||
geometries_kw : dict, optional | ||||||||||||||
Arguments passed to cartopy ax.add_geometry() which adds given geometries (GeoDataFrame geometry) to axis. | ||||||||||||||
legend_kw : dict, optional | ||||||||||||||
Arguments to pass to `ax.legend()`. | ||||||||||||||
legend_kw : dict or boolean, optional | ||||||||||||||
Arguments to pass to `ax.legend()`. No legend is added if legend_kw == False. | ||||||||||||||
show_time : bool, tuple, string or int. | ||||||||||||||
If True, show time (as date) at the bottom right of the figure. | ||||||||||||||
Can be a tuple of axis coordinates (0 to 1, as a fraction of the axis length) representing the location | ||||||||||||||
|
@@ -2169,24 +2174,23 @@ def hatchmap( | |||||||||||||
|
||||||||||||||
dattrs = None | ||||||||||||||
plot_data = {} | ||||||||||||||
dc = plot_kw.copy() | ||||||||||||||
|
||||||||||||||
# convert data to dict (if not one) | ||||||||||||||
if not isinstance(data, dict): | ||||||||||||||
if isinstance(data, xr.DataArray): | ||||||||||||||
plot_data = {data.name: data} | ||||||||||||||
if list(data.keys())[0] not in plot_kw.keys(): | ||||||||||||||
plot_kw = {list(plot_data.keys())[0]: dc} | ||||||||||||||
if data.name not in plot_kw.keys(): | ||||||||||||||
plot_kw = {data.name: plot_kw} | ||||||||||||||
elif isinstance(data, xr.Dataset): | ||||||||||||||
dattrs = data | ||||||||||||||
plot_data = {var: data[var] for var in data.data_vars} | ||||||||||||||
for v in plot_data.keys(): | ||||||||||||||
if v not in plot_kw.keys(): | ||||||||||||||
plot_kw[v] = dc | ||||||||||||||
plot_kw[v] = plot_kw | ||||||||||||||
else: | ||||||||||||||
for k, v in data.items(): | ||||||||||||||
if k not in plot_kw.keys(): | ||||||||||||||
plot_kw[k] = dc | ||||||||||||||
plot_kw[k] = plot_kw | ||||||||||||||
if isinstance(v, xr.Dataset): | ||||||||||||||
dattrs = k | ||||||||||||||
plot_data[k] = v[list(v.data_vars)[0]] | ||||||||||||||
|
@@ -2208,28 +2212,25 @@ def hatchmap( | |||||||||||||
if transform and ( | ||||||||||||||
"xlim" in list(plot_kw.values())[0] and "ylim" in list(plot_kw.values())[0] | ||||||||||||||
): | ||||||||||||||
extend = [ | ||||||||||||||
extent = [ | ||||||||||||||
list(plot_kw.values())[0]["xlim"][0], | ||||||||||||||
list(plot_kw.values())[0]["xlim"][1], | ||||||||||||||
list(plot_kw.values())[0]["ylim"][0], | ||||||||||||||
list(plot_kw.values())[0]["ylim"][1], | ||||||||||||||
] | ||||||||||||||
{v.pop("xlim") for v in plot_kw.values()} | ||||||||||||||
{v.pop("ylim") for v in plot_kw.values()} | ||||||||||||||
[v.pop(lim) for lim in ["xlim", "ylim"] for v in plot_kw.values() if lim in v] | ||||||||||||||
|
||||||||||||||
elif transform and ( | ||||||||||||||
"xlim" in list(plot_kw.values())[0] or "ylim" in list(plot_kw.values())[0] | ||||||||||||||
): | ||||||||||||||
extend = None | ||||||||||||||
extent = None | ||||||||||||||
warnings.warn( | ||||||||||||||
"Requires both xlim and ylim with 'transform'. Xlim or ylim was dropped" | ||||||||||||||
) | ||||||||||||||
if "xlim" in list(plot_kw.values())[0].keys(): | ||||||||||||||
{v.pop("xlim") for v in plot_kw.values()} | ||||||||||||||
if "ylim" in list(plot_kw.values())[0].keys(): | ||||||||||||||
{v.pop("ylim") for v in plot_kw.values()} | ||||||||||||||
[v.pop(lim) for lim in ["xlim", "ylim"] for v in plot_kw.values() if lim in v] | ||||||||||||||
|
||||||||||||||
else: | ||||||||||||||
extend = None | ||||||||||||||
extent = None | ||||||||||||||
|
||||||||||||||
# setup fig, ax | ||||||||||||||
if ax is None and ( | ||||||||||||||
|
@@ -2243,11 +2244,11 @@ def hatchmap( | |||||||||||||
): | ||||||||||||||
raise ValueError("Cannot use 'ax' and 'col'/'row' at the same time.") | ||||||||||||||
elif ax is None: | ||||||||||||||
{ | ||||||||||||||
[ | ||||||||||||||
v.setdefault("subplot_kws", {}).setdefault("projection", projection) | ||||||||||||||
for v in plot_kw.values() | ||||||||||||||
} | ||||||||||||||
cfig_kw = fig_kw.copy() | ||||||||||||||
] | ||||||||||||||
cfig_kw = copy.deepcopy(fig_kw) | ||||||||||||||
if "figsize" in fig_kw: # add figsize to plot_kw for facetgrid | ||||||||||||||
plot_kw[0].setdefault("figsize", fig_kw["figsize"]) | ||||||||||||||
cfig_kw.pop("figsize") | ||||||||||||||
|
@@ -2295,9 +2296,9 @@ def hatchmap( | |||||||||||||
im = v.where(mask is not True).plot.contourf(**plot_kw[k]) | ||||||||||||||
artists, labels = im.legend_elements(str_format="{:2.1f}".format) | ||||||||||||||
|
||||||||||||||
if ax: | ||||||||||||||
if ax and legend_kw: | ||||||||||||||
ax.legend(artists, labels, **legend_kw) | ||||||||||||||
else: | ||||||||||||||
elif legend_kw: | ||||||||||||||
im.figlegend = im.fig.legend(**legend_kw) | ||||||||||||||
|
||||||||||||||
elif len(plot_data) > 1 and "levels" in plot_kw[k]: | ||||||||||||||
|
@@ -2311,6 +2312,13 @@ def hatchmap( | |||||||||||||
if "hatches" not in plot_kw[k].keys(): | ||||||||||||||
plot_kw[k]["hatches"] = dfh[n] | ||||||||||||||
n += 1 | ||||||||||||||
elif isinstance( | ||||||||||||||
plot_kw[k]["hatches"], str | ||||||||||||||
): # make sure the hatches are in a list | ||||||||||||||
warnings.warn( | ||||||||||||||
"Hatches argument must be of type 'list'. Wrapping string argument as list." | ||||||||||||||
vindelico marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
) | ||||||||||||||
plot_kw[k]["hatches"] = [plot_kw[k]["hatches"]] | ||||||||||||||
|
||||||||||||||
plot_kw[k].setdefault("transform", transform) | ||||||||||||||
if ax: | ||||||||||||||
|
@@ -2344,31 +2352,31 @@ def hatchmap( | |||||||||||||
geometries_kw, | ||||||||||||||
frame, | ||||||||||||||
) | ||||||||||||||
if extend: | ||||||||||||||
fax.set_extent(extend) | ||||||||||||||
if extent: | ||||||||||||||
fax.set_extent(extent) | ||||||||||||||
|
||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
pat_leg.append( | ||||||||||||||
matplotlib.patches.Patch( | ||||||||||||||
hatch=plot_kw[k]["hatches"], fill=False, label=k | ||||||||||||||
hatch=plot_kw[k]["hatches"][0], fill=False, label=k | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||
) | ||||||||||||||
) | ||||||||||||||
|
||||||||||||||
if pat_leg: | ||||||||||||||
if pat_leg and legend_kw: | ||||||||||||||
legend_kw = { | ||||||||||||||
"loc": "lower right", | ||||||||||||||
"handleheight": 2, | ||||||||||||||
"handlelength": 4, | ||||||||||||||
} | legend_kw | ||||||||||||||
|
||||||||||||||
if ax: | ||||||||||||||
if ax and legend_kw: | ||||||||||||||
ax.legend(handles=pat_leg, **legend_kw) | ||||||||||||||
else: | ||||||||||||||
elif legend_kw: | ||||||||||||||
im.figlegend = im.fig.legend(handles=pat_leg, **legend_kw) | ||||||||||||||
|
||||||||||||||
# add features | ||||||||||||||
if ax: | ||||||||||||||
if extend: | ||||||||||||||
ax.set_extend(extend) | ||||||||||||||
if extent: | ||||||||||||||
ax.set_extent(extent) | ||||||||||||||
if dattrs: | ||||||||||||||
use_attrs.setdefault("title", "description") | ||||||||||||||
|
||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe use can instead of must