-
Notifications
You must be signed in to change notification settings - Fork 392
Description
While I don't think this is necessarily a bug, I'm having trouble finding a simple way to enforce a matplotlib Axes box aspect ratio without losing certain cartopy features from the map, depending on what data I'm plotting. An example is below:
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import matplotlib.pyplot as plot
import pandas as pd
MAP_FIXED_WIDTH = 8
MAP_FIXED_HEIGHT = 6
df = pd.DataFrame(
{
"timestamp":
[
pd.Timestamp('2025-01-01 00:00:00 UTC'),
pd.Timestamp('2025-01-01 00:30:00 UTC'),
pd.Timestamp('2025-01-01 01:00:00 UTC'),
pd.Timestamp('2025-01-01 01:30:00 UTC'),
pd.Timestamp('2025-01-01 02:00:00 UTC'),
],
"latitude": [-10, -12, -14, -16, -18],
"longitude": [40, 50, 60, 70, 80],
},
)
def create_map_with_scatter_points(df: pd.DataFrame) -> None:
plot.figure(figsize=(MAP_FIXED_WIDTH, MAP_FIXED_HEIGHT))
axes = plot.axes(
projection=ccrs.PlateCarree(central_longitude=df["longitude"].median())
)
axes.set_box_aspect(MAP_FIXED_HEIGHT / MAP_FIXED_WIDTH)
axes.scatter(df["longitude"], df["latitude"], color='green')
axes.set_facecolor(cfeature.COLORS["water"])
axes.add_feature(cfeature.LAND, color="lightgray")
axes.add_feature(cfeature.LAKES)
axes.add_feature(cfeature.RIVERS)
axes.add_feature(cfeature.COASTLINE)
axes.add_feature(cfeature.BORDERS, linestyle=":")
axes.gridlines(
draw_labels=True, linewidth=1, color="gray", alpha=0.5, linestyle="--"
)
plot.show()
create_map_with_scatter_points(df)
Output is as follows:
From the output, you'll see that certain COASTLINE features are not being rendered. Simply setting edgecolor of LAND to "black" would resolve the missing coastlines in this case, however there are other cases where landmasses don't get rendered based on the data extent and area of the world that we're plotting. My understanding is that Cartopy is unaware that it needs to render map features outside of the explicit range of the data being plotted.
I'm aware that axes.set_box_aspect() only controls the physical dimensions of the Axes box, not the limits of data being displayed. I've also tried using axes.set_aspect(adjustable='datalim'), though it comes with the caveat that set_adjustable method "may ignore explicitly defined axis limits" (link)
I'm explicitly trying to avoid having to set Axes extent manually, as the logic required for choosing the correct central longitude for the map when data crosses the 180th meridian is very cumbersome.
It would be great if there was an easier way to adjust the data limits based on the aspect ratio set with set_box_aspect(). Has anyone run into this quirk before and found a workaround?
Dependency versions:
matplotlib==3.10.7
pandas==2.3.3
cartopy==0.25.0