I'm trying to plot a triangular grid with cartopy and matplotlib.tri. I use a matplotlib.tri.Triangulation
object and would like to plot it with matplotlib.pyplot.triplot
.
When I pass the cartopy projection as a transformation to triplot with transform=projection
, as I would do for plotting a line, not all triangles are drawn and I get an IndexError
when trying to save the figure.
On the other hand, when I transform all points in the triangulation manually and call triplot, it works.
In the first example below, all triangles are drawn and the Line2D
objects do not differ. But trying to save the figure raises an IndexError
.
In the second example, the resulting Line2D
objects from triplot with transform=projection
and from triplot after the manual transformation differ in the result of their transformations applied to their data. So maybe it's a problem of the order of combined transformations.
N.B.: I chose PlateCarree to keep it as simple as possible. I had the same problem with other combinations of axes and data projections.
import matplotlib.pyplot as plt
import numpy as np
from cartopy.crs import PlateCarree
from matplotlib.tri import Triangulation
plt.interactive(False)
# NB. plt.triplot returns a list of two Line2D objects in both cases
# the second of which is empty, therefore only the first is returned
def triplot_with_transform(triangulation):
"""
triangulation: matplotlib.tri.Triangulation
"""
plt.figure()
plt.subplot(projection=PlateCarree())
lines = plt.triplot(triangulation, transform=PlateCarree())
return plt.gcf(), lines[0]
def transform_before_triplot(triangulation):
"""
triangulation: matplotlib.tri.Triangulation
"""
plt.figure()
plt.subplot(projection=PlateCarree())
[x_tr, y_tr, _] = PlateCarree().transform_points(PlateCarree(),
triangulation.x,
triangulation.y).T
triangulation_tr = Triangulation(x_tr, y_tr, triangulation.triangles)
lines = plt.triplot(triangulation_tr)
return plt.gcf(), lines[0]
def compare_line2d(line1, line2):
"""
line1, line2 : matplotlib.lines.Line2D
"""
data1 = line1.get_xydata()
transform1 = line1.get_transform()
data2 = line2.get_xydata()
transform2 = line2.get_transform()
data1_ma = np.ma.masked_invalid(data1)
data2_ma = np.ma.masked_invalid(data2)
data1_tr = transform1.transform(data1)
data2_tr = transform2.transform(data2)
data1_tr_ma = np.ma.masked_invalid(data1_tr)
data2_tr_ma = np.ma.masked_invalid(data2_tr)
print 'NaNs in line data match ', np.all(data1_ma.mask == data2_ma.mask)
print 'Line data mismatch', abs(data1_ma-data2_ma).max()
print 'NaNs in transformed line data match ', np.all(data1_tr_ma.mask ==
data2_tr_ma.mask)
print 'Transformed line data mismatch', abs(data1_tr_ma-data2_tr_ma).max()
def try_so_save(fig):
try:
fig.savefig('triangulation.pdf')
except Exception as e:
print "Exception while saving figure\n", e.__repr__()
# First example, 100 vertices.
x, y = np.meshgrid(np.arange(0, 50, 5), np.arange(0, 50, 5))
tri = Triangulation(x.ravel(), y.ravel())
fig1, line1 = triplot_with_transform(tri)
fig2, line2 = transform_before_triplot(tri)
# Second example, 2500 vertices.
x, y = np.meshgrid(np.arange(0, 50), np.arange(0, 50))
tri = Triangulation(x.ravel(), y.ravel())
fig3, line3 = triplot_with_transform(tri)
fig4, line4 = transform_before_triplot(tri)
# Compare Line2D objects; since we transform from PlateCarree to
# PlateCarree, the lines should be (almost) equal.
print "Line2D objects from first example"
compare_line2d(line1, line2)
print "Line2D objects from second example"
compare_line2d(line3, line4)
# Try to save the figures.
print "Save fig1 from first example."
try_so_save(fig1)
print "Save fig3 from second example."
try_so_save(fig3)
# failing plt.savefig command to produce full traceback
fig1.savefig('triangulation.pdf')
The output is
Line2D objects from first example
NaNs in line data match True
Line data mismatch 3.5527136788e-15
NaNs in transformed line data match True
Transformed line data mismatch 0.0
Line2D objects from second example
NaNs in line data match True
Line data mismatch 7.1054273576e-15
NaNs in transformed line data match True
Transformed line data mismatch 3545.955
Save fig1 from first example.
Exception while saving figure
IndexError('Out of bounds on buffer access (axis 0)',)
Save fig3 from second example.
Exception while saving figure
IndexError('Out of bounds on buffer access (axis 0)',)
When trying to save any of the figures created with transform=projection
, cartopy._crs.CRS.transform_points
raises the IndexError
.
Is this a problem of cartopy or matplotlib.tri? And is there a way to avoid the manual transformation and pass a transform
argument to triplot?
Edit:
After pp-mo's answer, I found that adding a format string with a marker, like 'o-'
, to the triplot command makes it work. Just changing the color ('g-
) does not help, though.
Edit 2:
Added fig1.savefig
in the example code to produce full traceback.