Skip to content

Commit

Permalink
add option to use arbitrary geometries as map boundary
Browse files Browse the repository at this point in the history
  • Loading branch information
raphaelquast committed Jun 23, 2024
1 parent db451fb commit 3e76ec0
Showing 1 changed file with 60 additions and 6 deletions.
66 changes: 60 additions & 6 deletions eomaps/eomaps.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.path import Path as mpath
import matplotlib.path as mpath

from cartopy import crs as ccrs

Expand Down Expand Up @@ -1266,16 +1266,53 @@ def set_extent_to_location(self, location, annotate=False, user_agent=None):
else:
_log.info(f"Centering Map to:\n {r['display_name']}")

def set_frame(self, rounded=0, **kwargs):
def _set_gdf_path_boundary(self, gdf, set_extent=True):
geom = gdf.to_crs(self.crs_plot).unary_union.boundary

Check warning on line 1270 in eomaps/eomaps.py

View check run for this annotation

Codecov / codecov/patch

eomaps/eomaps.py#L1270

Added line #L1270 was not covered by tests

if geom.geom_type == "MultiLineString":
boundary_linestrings = geom.geoms
elif geom.geom_type == "LineString":
boundary_linestrings = [geom]

Check warning on line 1275 in eomaps/eomaps.py

View check run for this annotation

Codecov / codecov/patch

eomaps/eomaps.py#L1272-L1275

Added lines #L1272 - L1275 were not covered by tests

vertices, codes = [], []
for g in boundary_linestrings:
x, y = g.xy
codes.extend(

Check warning on line 1280 in eomaps/eomaps.py

View check run for this annotation

Codecov / codecov/patch

eomaps/eomaps.py#L1277-L1280

Added lines #L1277 - L1280 were not covered by tests
[mpath.Path.MOVETO, *[mpath.Path.LINETO] * len(x), mpath.Path.CLOSEPOLY]
)
vertices.extend([(x[0], y[0]), *zip(x, y), (x[-1], y[-1])])

Check warning on line 1283 in eomaps/eomaps.py

View check run for this annotation

Codecov / codecov/patch

eomaps/eomaps.py#L1283

Added line #L1283 was not covered by tests

path = mpath.Path(vertices, codes)

Check warning on line 1285 in eomaps/eomaps.py

View check run for this annotation

Codecov / codecov/patch

eomaps/eomaps.py#L1285

Added line #L1285 was not covered by tests

self.ax.set_boundary(path, self.ax.transData)
if set_extent:
x0, y0 = np.min(vertices, axis=0)
x1, y1 = np.max(vertices, axis=0)

Check warning on line 1290 in eomaps/eomaps.py

View check run for this annotation

Codecov / codecov/patch

eomaps/eomaps.py#L1287-L1290

Added lines #L1287 - L1290 were not covered by tests

self.set_extent([x0, x1, y0, y1], gdf.crs)

Check warning on line 1292 in eomaps/eomaps.py

View check run for this annotation

Codecov / codecov/patch

eomaps/eomaps.py#L1292

Added line #L1292 was not covered by tests

def set_frame(self, rounded=0, gdf=None, set_extent=True, **kwargs):
"""
Set the properties of the map boundary and the background patch.
You can either use a rectangle with rounded corners or arbitrary geometries
provided as a geopandas.GeoDataFrame.
Parameters
----------
rounded : float, optional
If provided, use a rectangle with rounded corners as map boundary
line. The corners will be rounded with respect to the provided
fraction (0=no rounding, 1=max. radius). The default is None.
gdf : geopandas.GeoDataFrame or path
A geopandas.GeoDataFrame that contains geometries that should be used as
map-frame.
If a path (string or pathlib.Path) is provided, the corresponding file
will be read as a geopandas.GeoDataFrame and the boundaries of the
contained geometries will be used as map-boundary.
The default is None.
kwargs :
Additional kwargs to style the boundary line (e.g. the spine)
and the background patch
Expand All @@ -1298,6 +1335,8 @@ def set_frame(self, rounded=0, **kwargs):
>>> m.add_feature.preset.ocean()
>>> m.set_frame(fc="r", ec="b", lw=3, rounded=.2)
Customize the map-boundary style
>>> import matplotlib.patheffects as pe
>>> m = Maps()
>>> m.add_feature.preset.ocean(fc="k")
Expand All @@ -1306,6 +1345,15 @@ def set_frame(self, rounded=0, **kwargs):
>>> rounded=.5,
>>> path_effects=[pe.withStroke(linewidth=7, foreground="m")])
Set the map-boundary to a custom polygon (in this case the boarder of Austria)
>>> m = Maps()
>>> m.add_feature.preset.land(fc="k")
>>> # Get a GeoDataFrame with all country-boarders from NaturalEarth
>>> gdf = m.add_feature.cultural.admin_0_countries.get_gdf()
>>> # set the map-boundary to the Austrian country-boarder
>>> m.set_frame(gdf = gdf[gdf.NAME=="Austria"])
"""

for key in ("fc", "facecolor"):
Expand All @@ -1314,7 +1362,14 @@ def set_frame(self, rounded=0, **kwargs):

self.ax.spines["geo"].update(kwargs)

if rounded:
if gdf is not None:
assert (

Check warning on line 1366 in eomaps/eomaps.py

View check run for this annotation

Codecov / codecov/patch

eomaps/eomaps.py#L1366

Added line #L1366 was not covered by tests
rounded == 0
), "EOmaps: using rounded > 0 is not supported for gdf frames!"

self._set_gdf_path_boundary(self._handle_gdf(gdf), set_extent=set_extent)

Check warning on line 1370 in eomaps/eomaps.py

View check run for this annotation

Codecov / codecov/patch

eomaps/eomaps.py#L1370

Added line #L1370 was not covered by tests

elif rounded:
assert (
rounded <= 1
), "EOmaps: rounded corner fraction must be between 0 and 1"
Expand Down Expand Up @@ -1366,14 +1421,13 @@ def cb(*args, **kwargs):
y0 + r,
]

path = mpath(np.column_stack((xs, ys)))
path = mpath.Path(np.column_stack((xs, ys)))
self.ax.set_boundary(path, transform=self.crs_plot)

self.BM._before_fetch_bg_actions.append(cb)
self.ax._EOmaps_rounded_spine_attached = True

self.redraw("__SPINES__")
self.redraw("__BG__")
self.redraw()

@staticmethod
def set_clipboard_kwargs(**kwargs):
Expand Down

0 comments on commit 3e76ec0

Please sign in to comment.