Iris has a built-in
Trajectory
class that is very easy to use. You have to enter the waypoints and the number
of linear spaced points between them. Imagine a np.linspace
for lon, lat
points. Below I will demonstrate with a simple use to extract
bathymetric values from
ETOPO2.
import iris
url = "http://opendap.ccst.inpe.br/Misc/etopo2/ETOPO2v2c_f4.nc"
cube = iris.load_cube(url)
print(cube)
The cube has global data, let's reduce it to a smaller area.
lon = iris.Constraint(x=lambda cell: -54 <= cell <= -20)
lat = iris.Constraint(y=lambda cell: -30 <= cell <= -12)
cube = cube.extract(lon & lat)
print(cube)
Here is how to construct a trajectory.
from iris.analysis import trajectory
waypoints = [{'x': -38.5, 'y': -20.65}, {'x': -28.5, 'y': -20.65}]
#[(-38.594367271025789, -20.40487006995896),
#(-28.695012274806029, -20.531784877602803)]
traj = trajectory.Trajectory(waypoints, sample_count=30)
traj
traj.sampled_points[:5] # First 5 points
traj.length * 111 # Approximate distance in km.
Here is where iris is a little bit annoying. The
trajectory.interpolate
seems to be the intended consumer of this trajectory object, but interpolate
takes a tuple with x
and y
points, while the Trajectory object is a list
of dictionaries. I am sure there is a reason for that!
(I just cannot see it ;-)
Anyways, we can quickly transform this into the input expected by interpolate.
lon = [d['x'] for d in traj.sampled_points]
lat = [d['y'] for d in traj.sampled_points]
sampled_points = [('x', lon),
('y', lat)]
section = trajectory.interpolate(cube, sampled_points)
print(section)
Not that the interpolated section data is a cube. Lovely! Now all that is to plot the section we extracted.
import numpy as np
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
from palettable import colorbrewer
from cartopy.mpl.gridliner import LONGITUDE_FORMATTER, LATITUDE_FORMATTER
land = colorbrewer.get_map('Greens', 'sequential', 9)
ocean = colorbrewer.get_map('Blues', 'sequential', 9, reverse=True)
colors = np.array(ocean.mpl_colors + land.mpl_colors)
levels = [-9000, -6000, -5000, -4000, -3000, -2500, -1000, -700, -400, -145, -10, 0, 10, 145, 400, 800, 1200, 1600]
def make_map(projection=ccrs.PlateCarree()):
fig, ax = plt.subplots(figsize=(8, 8), subplot_kw=dict(projection=projection))
ax.coastlines(resolution='50m')
gl = ax.gridlines(draw_labels=True)
gl.xlabels_top = gl.ylabels_right = False
gl.xformatter = LONGITUDE_FORMATTER
gl.yformatter = LATITUDE_FORMATTER
return fig, ax
import seawater as sw
from matplotlib.ticker import MaxNLocator
lons = cube.coord('x').points
lats = cube.coord('y').points
dist = np.r_[0, sw.dist(lat, lon)[0].cumsum()]
fig, ax = make_map(projection=ccrs.PlateCarree())
offset = 1
ax.set_extent([lons.min()+offset, lons.max()-offset,
lats.min()+offset, lats.max()-offset])
cs = ax.contourf(lons, lats, cube.data, levels=levels, colors=colors)
l, = ax.plot(lon, lat, 'r', linewidth=2, alpha=0.7)
inset_ax = fig.add_axes([0.47, 0.4, 0.26, 0.125], frameon=False)
c = inset_ax.fill_between(dist, -section.data, y2=5000, color="none",
hatch="//", edgecolor='k', interpolate=True)
inset_ax.yaxis.tick_right()
inset_ax.axis([0, 1090, -400, 4000])
inset_ax.xaxis.tick_bottom()
inset_ax.yaxis.tick_right()
inset_ax.invert_yaxis()
inset_ax.yaxis.set_major_locator(MaxNLocator(4))
inset_ax.xaxis.set_major_locator(MaxNLocator(5))
If you are curious here is the feature I am using for the example.
Have fun!
HTML(html)