I have a frustrating problem that only manifests itself when plotting filled contour plots on 3D axes and only in certain situations.
Here is an example of the issue I am experiencing:
and
These are the same data at different contouring intervals. You'll notice on the left side of the domain there is mis-filling occurring. This is a plot with the Z points squished into the Z=0 plane, via a plotting command like
ax3d.contourf(X, Y, dbz[z25,:,:], zdir='z', offset=0, levels=levels, cmap='pymeteo_radar', alpha=0.50)
The miscontouring happens regardless of alpha level or colormap used, but is sensitive to the number of levels. The use of zdir
and offset
do not effect the mis-contouring (the artifact just occurs on the Z surface. If I do not fill the contour, there is no mis-contouring. I can also alter the domain to sometimes make the issue better (or worse), but I have many plots to make within the same domain so that is not a fix.
This issue does not occur when the same data is plotted on 2D axes, e.g.:
This plot has some extra data on it, but you can see that the filled contouring does not have the same artifact from mis-filling the contour that occurs on the 3d axes.
Below is a script you can run to reproduce the issue.
#!/usr/bin/env python
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d.axes3d as p3
data=np.array([[53.9751, 51.5681, 50.7119, 51.1049, 51.5339, 51.4977, 51.2387,50.761, 50.1732, 49.8218, 49.5442, 48.936, 47.4498, 46.6484, 45.8542, 45.136, 44.5268, 44.071, 43.7665, 43.5928, 43.5269, 43.5385, 43.6053, 45.565, 47.0071, 46.8664, 47.372, 47.8324, 48.295, 48.731, 49.0522, 49.4001, 49.7111, 49.9919, 50.2527, 50.4928, 50.7135, 50.8831, 51.0806, 51.2683 ],
[55.6671, 52.53, 50.7764, 50.5632, 51.2095, 51.5659, 51.521, 51.2143, 50.653, 50.2371, 49.989, 49.8089, 49.6058, 47.8355, 47.3124, 46.7346, 46.1616, 45.6498, 45.2462, 44.967, 44.8005, 44.7284, 44.7295, 44.7869, 46.959, 45.0194, 46.73, 48.0766, 48.9395, 49.5325, 49.8498, 50.1887, 50.4798, 50.7406, 50.9808, 51.2003, 51.4074, 51.555, 51.7429, 51.9218 ],
[56.6513, 53.5919, 51.2774, 50.3133, 50.7705, 51.533, 51.8287, 51.7083, 51.2816, 50.7933, 50.4806, 50.2671, 50.1009, 50.0096, 49.9052, 49.4698, 47.4655, 47.0717, 46.6849, 46.3583, 46.1122, 45.952, 45.8678, 45.8485, 45.8811, 45.956, 46.0634, 47.2225, 49.4363, 50.2482, 50.527, 50.8558, 51.1358, 51.3809, 51.607, 51.8179, 52.0161, 52.1454, 52.3263, 52.497 ],
[57.078, 54.3224, 52.0759, 50.4679, 50.4677, 51.297, 52.0284, 52.1594, 51.9395, 51.5518, 51.1419, 50.8765, 50.6686, 50.5101, 50.4078, 50.3473, 50.3592, 50.3813, 49.7504, 47.55, 47.324, 47.1365, 46.9978, 46.9119, 46.8743, 46.8811, 46.9257, 47.0013, 50.0148, 50.9106, 51.1133, 51.4282, 51.7064, 51.943, 52.1587, 52.3597, 52.4789, 52.6631, 52.8359, 52.9966 ],
[57.3835, 54.9025, 52.8571, 50.9842, 50.5197, 51.1494, 52.0599, 52.4732, 52.4716, 52.2656, 51.9535, 51.6068, 51.3466, 51.1513, 50.9708, 50.8321, 50.7639, 50.7944, 50.8817, 49.8122, 48.2038, 48.086, 47.9704, 47.8735, 47.8035, 47.7644, 47.7574, 47.7803, 50.8194, 51.5486, 51.6645, 51.9745, 52.2349, 52.4508, 52.6481, 52.8317, 52.9412, 53.1097, 53.2699, 53.4171 ],
[57.9157, 55.6092, 53.6306, 51.8011, 50.9372, 51.2615, 52.1406, 52.7436, 52.8528, 52.7829, 52.6322, 52.403, 52.1149, 51.866, 51.6624, 51.4773, 51.317, 51.2183, 51.2153, 51.1367, 48.5913, 48.6216, 48.6218, 48.5951, 48.5589, 48.527, 48.5081, 50.5185, 51.6998, 51.905, 52.2258, 52.4891, 52.7062, 52.8926, 53.0655, 53.2251, 53.3262, 53.4755, 53.6169, 53.7471 ],
[58.6093, 56.432, 54.307, 52.6277, 51.584, 51.6482, 52.3762, 53.0685, 53.2545, 53.217, 53.1356, 53.0351, 52.8481, 52.6154, 52.39, 52.177, 51.9977, 51.843, 51.7172, 51.4587, 48.7481, 48.7984, 48.864, 48.9291, 48.9843, 49.0228, 50.496, 51.8667, 52.3404, 52.4759, 52.6889, 52.8851, 53.0525, 53.2072, 53.354, 53.4576, 53.5925, 53.7217, 53.8432, 53.956 ],
[58.9719, 56.9885, 54.8768, 53.3526, 52.3025, 52.2089, 52.7762, 53.4444, 53.6768, 53.6706, 53.5692, 53.5162, 53.4373, 53.2886, 53.1113, 52.9065, 52.6988, 52.5193, 52.3544, 52.0384, 48.9624, 48.9653, 49.0005, 49.0574, 49.1258, 50.692, 51.9726, 52.4309, 52.699, 52.8194, 52.9845, 53.1336, 53.2669, 53.393, 53.5118, 53.6086, 53.7213, 53.8293, 53.9308, 54.026 ],
[58.5754, 56.945, 55.068, 53.7798, 52.9469, 52.854, 53.3136,53.8929, 54.1205, 54.1178, 54.0128, 53.9289, 53.8906, 53.8239,53.717, 53.5724, 53.3818, 53.1892, 53.009, 49.3078, 49.2524,49.2165, 49.2032, 49.2187, 50.463, 51.9497, 52.4487, 52.7041,52.8358, 52.9776, 53.1101, 53.2293, 53.3419, 53.4487, 53.5401,53.6365, 53.7301, 53.8205, 53.9062, 53.9869 ],
[57.623, 56.547, 55.0117, 54.0512, 53.5372, 53.5246, 53.927,54.3868, 54.5828, 54.5811, 54.4501, 54.3235, 54.2626, 54.2334,54.1802, 54.1137, 53.9897, 53.8202, 49.796, 49.6864, 49.5946,49.5216, 49.4703, 49.4432, 51.8479, 52.5574, 52.8359, 52.9722,53.0827, 53.1826, 53.2747, 53.3597, 53.4405, 53.5138, 53.5944,53.6751, 53.7536, 53.829, 53.9019, 53.9721 ],
[56.902, 56.0005, 54.9159, 54.3352, 54.123, 54.2014, 54.5659,54.8917, 55.0307, 55.0139, 54.8838, 54.7044, 54.5863, 54.5548,54.5258, 54.4957, 54.4633, 51.4821, 50.1897, 50.0758, 49.9683,49.8704, 49.7842, 51.5064, 52.7625, 53.0724, 53.1926, 53.2682,53.3404, 53.4119, 53.4831, 53.5517, 53.6169, 53.6763, 53.7383,53.8009, 53.8644, 53.9281, 53.9905, 54.0517 ],
[56.3455, 55.5524, 54.9336, 54.6836, 54.703, 54.8657, 55.1749,55.3844, 55.4521, 55.4019, 55.2622, 55.0281, 54.8981, 54.6591,54.7866, 54.7678, 54.7654, 54.0436, 54.2302, 52.2533, 50.3305,50.2276, 50.1268, 52.9617, 53.4395, 53.5504, 53.5481, 53.5524,53.5699, 53.6014, 53.644, 53.6931, 53.7445, 53.7996, 53.8548,53.9097, 53.9655, 54.0229, 54.0813, 54.1393 ],
[55.7493, 55.3019, 55.1012, 55.0906, 55.234, 55.4751, 55.7134,55.8462, 55.8461, 55.7425, 55.5725, 55.3535, 55.1612, 54.958,55.0193, 54.9584, 54.9531, 54.8886, 54.8256, 54.2211, 50.6477,50.5564, 53.0546, 53.8592, 54.08, 54.0288, 53.9509, 53.8796,53.8307, 53.8073, 53.8034, 53.8142, 53.8383, 53.8725, 53.9128,53.9558, 54.0013, 54.0497, 54.103, 54.1597 ],
[55.2575, 55.1664, 55.3165, 55.5004, 55.7345, 55.9901, 56.1852,56.2599, 56.2027, 56.0454, 55.818, 55.5754, 55.302, 55.2083,55.0224, 55.1415, 55.0656, 55.0446, 55.0263, 54.7728, 50.8924,53.4671, 54.2587, 54.5146, 54.6171, 54.519, 54.3857, 54.2497,54.1355, 54.0509, 53.9932, 53.9584, 53.941, 53.939, 53.9527,53.9798, 54.0111, 54.0465, 54.0868, 54.1339 ],
[54.8665, 55.1533, 55.5095, 55.8512, 56.1541, 56.3995, 56.5593,56.6009, 56.5079, 56.3001, 56.0178, 55.7187, 55.448, 55.063,55.2016, 55.2116, 55.1817, 55.112, 55.1099, 55.0299, 54.3358,54.6966, 54.9199, 55.0156, 55.0728, 54.975, 54.8299, 54.6609,54.493, 54.3475, 54.2349, 54.1517, 54.0928, 54.0516, 54.0245,54.013, 54.0206, 54.0404, 54.0667, 54.0989 ],
[54.2676, 55.1132, 55.6112, 56.09, 56.428, 56.6661, 56.8056,56.8374, 56.7339, 56.4923, 56.1474, 55.7977, 55.4805, 55.2341,54.8999, 55.2662, 55.2927, 55.185, 55.1237, 55.1268, 54.9772,55.1418, 55.2612, 55.3333, 55.379, 55.3244, 55.2153, 55.0629,54.881, 54.6926, 54.523, 54.3866, 54.2855, 54.2118, 54.1583,54.1191, 54.0935, 54.0834, 54.0885, 54.1057 ],
[54.1771, 55.0795, 55.7075, 56.1772, 56.5183, 56.7522, 56.8898,56.9315, 56.8427, 56.6056, 56.2317, 55.8095, 55.4436, 55.183,55.0284, 54.9504, 55.2833, 55.2563, 55.1498, 55.1342, 55.1331,55.259, 55.3705, 55.4452, 55.4955, 55.5087, 55.4697, 55.3766,55.2324, 55.049, 54.8485, 54.6578, 54.4995, 54.3822, 54.3002,54.2427, 54.2022, 54.1749, 54.1598, 54.1561 ],
[53.9112, 54.85, 55.6641, 56.0844, 56.4062, 56.6232, 56.757,56.8149, 56.7669, 56.5754, 56.2311, 55.785, 55.366, 55.0104,54.812, 54.8845, 55.1273, 55.2339, 55.1976, 55.1049, 55.0913,55.1843, 55.3048, 55.4076, 55.4709, 55.518, 55.5455, 55.5329,55.4636, 55.3349, 55.1595, 54.9529, 54.7462, 54.5681, 54.4342,54.3439, 54.2848, 54.2446, 54.2222, 54.2135 ],
[53.9368, 54.9196, 55.4408, 55.7999, 56.0652, 56.2423, 56.348,56.4106, 56.4114, 56.3028, 56.0519, 55.6779, 55.2493, 54.8836,54.6592, 54.6347, 54.8341, 55.0606, 55.1396, 55.0967, 55.0325,55.0501, 55.1451, 55.2627, 55.3559, 55.4216, 55.4789, 55.5183,55.5245, 55.4779, 55.3701, 55.2072, 55.0029, 54.7876, 54.5915,54.4378, 54.3368, 54.2787, 54.2415, 54.2271 ],
[53.9325, 54.6506, 55.0421, 55.2926, 55.4603, 55.5679, 55.6285,55.6792, 55.7234, 55.731, 55.639, 55.3923, 55.043, 54.6845,54.4188, 54.3242, 54.4606, 54.7449, 54.9548, 55.0171, 55.0047,54.9454, 54.9666, 55.0651, 55.1828, 55.2677, 55.3308, 55.3914,55.438, 55.4544, 55.4277, 55.3385, 55.1907, 54.9981, 54.7786,54.5691, 54.4013, 54.2898, 54.233, 54.1994 ] ])
fig = plt.figure()
ax = fig.add_subplot(111,projection='3d')
X,Y = np.meshgrid(np.arange(-30.0,-20.0,0.25), np.arange(20.0,25,0.25))
ax.contourf(X,Y,data,zdir='z',offset=0, levels=np.arange(0,75,1))
ax.set_zlim(0.0,2.0)
plt.savefig('testfig.png')
plt.close()
This code will produce the plot:
In all of the cases I have observed this mis-contouring the bad triangle always has a vertex near the bottom left of the domain. My data is regularly gridded and for the domain in question is uniform in X and Y. In this case the mis-filling will go away if the number of contour levels is reduced. In some other cases this does not always help or just changes the visual appearance of the error. In any case, even at very coarse contouring I still get errors in a subset of my plots.
Has anyone seen this before and found a fix for it? Am I overlooking something? I'm open to workarounds that don't involve lowering my contouring level (which does reduce the errors overall). If others are in agreement that this could be a bug in the mplot3d, I will file a bug report with them (Issue opened here). I have a feeling the problem lies with contouring very strong gradients when the levels
option causes dense contours, but oddly only on 3d axes.
Relevant version information:
The ax. contour3D() function creates three-dimensional contour plot. It requires all the input data to be in the form of two-dimensional regular grids, with the Z-data evaluated at each point.
A contour of the function z(x,y) is a set of points in the (x,y) plane, such that z(x,y) is fixed at some constant value. That constant value is the contour "level". That set of points in the (x,y) plane is often called a level set.
A contour plot is a graphical technique for representing a 3-dimensional surface by plotting constant z slices, called contours, on a 2-dimensional format. That is, given a value for z, lines are drawn for connecting the (x,y) coordinates where that z value occurs.
This turned out to be a longstanding bug in matplotlib.mplot3d that ignores path information when taking 2D contourf sets and extending them into 3D. This causes, under certain circumstances, paths with holes to render improperly when a path segment intended as a "move" is instead "drawn".
I contributed a fix for this issue to matplotlib and this bug is fixed in the matplotlib 1.5.0 stable release.
The same test code as in the question produces a correct plot with matplotlib 1.5, as seen below:
The problem is most probably in matplotlib itself and you're not doing anything wrong.
By experimenting a bit I found that if you multiply the input data by 1.01
or 0.999
the plot comes out right, but 1.001
or 0.9999
is not enough to fix the issue.
Adding or subtracting a constant instead shifts the color but keeps the problem evident.
As a wild guess some internal computation falls in a singularity (even if I cannot think what formula would be in danger in this case).
You should submit a bug to their tracker.
On a second thought may be matplotlib is trying to compute contour polygons instead of just computing a background texture on a texel-by-texel basis and this could result in annoying accuracy problems that depend on the value. Drawing contour lines is instead much easier because you can just compute the segments in a marching-square approach without worrying about rebuilding the full contour line topology (and for example if a very tiny segment is missing from the line contour plot you're not going to notice anyway).
If this is indeed the bug then may be the fix is not easy because requires a full reimplementation of the plane drawing in a completely different (even if easier) way.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With