Search code examples
matplotlibprintingmapscartopy

Map to fit postersize using Cartopy


My goal is to create a poster with a map of travel routs.

You should be able to set the poster size, i.e figure size.

Then you provide a list of long/lat points. Whether or not the poster should be horizontal or vertical should be up to the script, i.e what fits the lat/long provided.

It should then return the poster as a pdf, zoomed in on the relevant part of the map, while still filling the entire canvas. It’s been the last part I have struggled with for days. I guess the extend of the entire map needs to be changed to fit with the ratio of the poster size, but I haven’t been able to do so. Any suggestions would be much obliged ! PS. Im aware of (bbox_inches='tight'), but it ruins the total poster size.

Example script:

import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import numpy as np

# Define the geographic locations (longitude, latitude)
points = np.array([
    (18.4241, -33.9249),
    (9.5375, 33.8869),  
    (-16.3151, 28.4682),
    (-9.1393, 38.7223),
    (-3.7038, 40.4168),
    (2.3522, 48.8566),
    (10.7522, 59.9139),
])

# Create figure and add a map in Mercator projection
fig, ax = plt.subplots(figsize=(10, 10), subplot_kw={'projection': ccrs.Mercator()})

# Calculate bounds
lon_min, lat_min = points.min(axis=0) - 1
lon_max, lat_max = points.max(axis=0) + 1

# Set extent based on points
ax.set_extent([lon_min, lon_max, lat_min, lat_max], crs=ccrs.PlateCarree())

# Plot points
ax.scatter(points[:, 0], points[:, 1], marker='o', color='red', transform=ccrs.Geodetic())

# Add coastlines for reference
ax.coastlines()

# Save the plot as a PDF
plt.savefig("map.pdf")

Solution

  • You can use constrained layout to make an axes fill the figure, and set_adjustable to make the data limits update so that the map is the right shape for the expanded axes:

    import matplotlib.pyplot as plt
    import cartopy.crs as ccrs
    import numpy as np
    
    # Define the geographic locations (longitude, latitude)
    points = np.array([
        (18.4241, -33.9249),
        (9.5375, 33.8869),  
        (-16.3151, 28.4682),
        (-9.1393, 38.7223),
        (-3.7038, 40.4168),
        (2.3522, 48.8566),
        (10.7522, 59.9139),
    ])
    
    # Create figure and add a map in Mercator projection
    fig, ax = plt.subplots(figsize=(10, 10), subplot_kw={'projection': ccrs.Mercator()},
                           layout='constrained')
    
    # Calculate bounds
    lon_min, lat_min = points.min(axis=0) - 1
    lon_max, lat_max = points.max(axis=0) + 1
    
    # Set extent based on points
    ax.set_extent([lon_min, lon_max, lat_min, lat_max], crs=ccrs.PlateCarree())
    
    # Adjust the data limits so that the map fills the figure without stretching
    ax.set_adjustable(adjustable='datalim')
    
    # Plot points
    ax.scatter(points[:, 0], points[:, 1], marker='o', color='red', transform=ccrs.Geodetic())
    
    # Add coastlines for reference
    ax.coastlines()
    
    # Save the plot as a png (for StackOverflow example)
    plt.savefig("map.png")
    
    

    enter image description here