Tommy Lees
Tommy Lees

Reputation: 1373

embed small map (cartopy) on matplotlib figure

# imports
from collections import namedtuple
import numpy as np
import xarray as xr
import shapely
import cartopy

 The data that I have looks like this.

I have a region of interest (defined here as all_region). I have an xr.DataArray which contains my variable. What I want to do it to select one PIXEL (lat,lon pair) and to plot a small map in the corner of the lineplot showing here that pixel is located.

Region = namedtuple('Region',field_names=['region_name','lonmin','lonmax','latmin','latmax'])
all_region = Region(
    region_name="all_region",
    lonmin = 32.6,
    lonmax = 51.8,
    latmin = -5.0,
    latmax = 15.2,
)

data = np.random.normal(0,1,(12, 414, 395))
lats = np.linspace(-4.909738, 15.155708, 414)
lons = np.linspace(32.605801, 51.794488, 395)
months = np.arange(1,13)
da = xr.DataArray(data, coords=[months, lats, lons], dims=['month','lat','lon'])

These are the functions that I need to fix to work with inset axes.

I have these functions which plot my timeseries from the xarray object, and also the location of the lat,lon point.

def plot_location(region):
    """ use cartopy to plot the region (defined as a namedtuple object)
    """
    lonmin,lonmax,latmin,latmax = region.lonmin,region.lonmax,region.latmin,region.latmax
    fig = plt.figure()
    ax = fig.gca(projection=cartopy.crs.PlateCarree())
    ax.add_feature(cartopy.feature.COASTLINE)
    ax.add_feature(cartopy.feature.BORDERS, linestyle=':')
    ax.set_extent([lonmin, lonmax, latmin, latmax])

    return fig, ax


def select_pixel(ds, loc):
    """ (lat,lon) """
    return ds.sel(lat=loc[1],lon=loc[0],method='nearest')


def turn_tuple_to_point(loc):
    """ (lat,lon) """
    from shapely.geometry.point import Point
    point = Point(loc[1], loc[0])
    return point


def add_point_location_to_map(point, ax, color=(0,0,0,1), **kwargs):
    """ """
    ax.scatter(point.x,
           point.y,
           transform=cartopy.crs.PlateCarree(),
           c=[color],
           **kwargs)
    return

Here I do the plotting

# choose a lat lon location that want to plot
loc = (2.407,38.1)

# 1. plot the TIME SERIES FOR THE POINT
fig,ax = plt.subplots()
pixel_da = select_pixel(da, loc)
pixel_da.plot.line(ax=ax, marker='o')

# 2. plot the LOCATION for the point
fig,ax = plot_location(all_region)
point = turn_tuple_to_point(loc)
add_point_location_to_map(point, ax)

Plot 1

Plot 2

 I have my function for plotting a region, but I want to put this on an axis in the corner of my figure! Like this:

Ideal output

How would I go about doing this? I have had a look at the inset_locator method but as far as I can tell the mpl_toolkits.axes_grid1.parasite_axes.AxesHostAxes has no means of assigning a projection, which is required for cartopy.

from mpl_toolkits.axes_grid1.inset_locator import inset_axes

proj=cartopy.crs.PlateCarree
axins = inset_axes(ax, width="20%", height="20%", loc=2, projection=proj)

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-162-9b5fd4f34c3e> in <module>
----> 1 axins = inset_axes(ax, width="20%", height="20%", loc=2, projection=proj)

TypeError: inset_axes() got an unexpected keyword argument 'projection'

Upvotes: 6

Views: 3754

Answers (2)

ImportanceOfBeingErnest
ImportanceOfBeingErnest

Reputation: 339250

The mpl_toolkits.axes_grid1.inset_locator.inset_axes does not have a projection keyword. It only provides an axes_class argument. Now one might be tempted to provide the cartopy.mpl.geoaxes.GeoAxes directly to that argument, yet this would be missing the actual projection in use. So in addition one needs to set the projection via the axes_kwargs argument.

inset_axes(...,  axes_class=cartopy.mpl.geoaxes.GeoAxes, 
                 axes_kwargs=dict(map_projection=cartopy.crs.PlateCarree()))

Complete example:

import cartopy
import cartopy.mpl.geoaxes
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1.inset_locator import inset_axes


fig, ax = plt.subplots()
ax.plot([4,5,3,1,2])


axins = inset_axes(ax, width="40%", height="40%", loc="upper right", 
                   axes_class=cartopy.mpl.geoaxes.GeoAxes, 
                   axes_kwargs=dict(map_projection=cartopy.crs.PlateCarree()))
axins.add_feature(cartopy.feature.COASTLINE)


plt.show()

enter image description here

Upvotes: 17

Diziet Asahi
Diziet Asahi

Reputation: 40697

I don't have cartopy installed to test it directly, but I believe you can circumvent the problem by creating your inset Axes by hand directly, using fig.add_axes(). If you want to specify its position relative to the main axes, you can easily calculate the rect parameter using the info returned by the main axes get_position().

For example:

pad = 0.05
w = 0.4
h = 0.25

fig, ax = plt.subplots()
a = ax.get_position()
ax2 = fig.add_axes([a.x1-(w+pad)*a.width,a.y1-(h+pad)*a.height,w*a.width,h*a.height], projection="hammer")

enter image description here

Upvotes: 1

Related Questions