After updating to mpl 3.5.0 my program hangs after plotting with pcolormesh(), no error messages. It works fine witl mpl 3.3.3. When I reduce the nx,ny values in my program it works fine.
Code for reproduction
importnumpyasnpimportmatplotlib.pyplotaspltimportcartopy.io.img_tilesascimgtimportcartopy.crsasccrsfig, ax1=plt.subplots(num="TestXX", figsize=(30/2.54, 25/2.54))
#add a map of the Netherlandsrequest=cimgt.OSM()
ax1=plt.axes(projection=request.crs)
ax1.set_extent([3.25, 7.35, 50.45, 53.55],ccrs.PlateCarree())
ax1.add_image(request, 10)
#create a rectangular grid a little smaller than the map#with cell sizes of 0.01 degree filled with random valuesnx=400ny=300xedges=np.linspace(3.3, 7.3, nx)
yedges=np.linspace(50.5, 53.5, ny)
zvalues=np.random.random((nx, ny))
ax1.pcolormesh(xedges, yedges, zvalues.T ,\
alpha=0.3, shading='auto', transform=ccrs.PlateCarree())
plt.show()
Actual outcome
Program hangs. Toolbar doesn't response. Hourglass is visible. No errors.
Expected outcome
Plot has been made. Toolbar works, normal running program.
Additional information
It worked fine with matplotlib 3.3.3. I can then increase nx,ny to 8000,6000.(I didn't try higher values)
It seems that there is a 'problem' with pcolormesh().
It works fine in mpl 3.5.0 with reduced nx,ny values(i.e. 100,75)
3.5 changed the behaviour of pcolormesh when nx and ny were the same size; we used to drop the last row and column. Now we don't do that. In order to get the old behaviour you can drop the last row and column yourself, and user interp='flat'. However, I don't know how that interacts with cartopy.
Does the same behaviour happen without cartopy? Perhaps this is a cartopy problem, despite being linked to a Matplotlib version change. You may also make sure you are using the latest cartopy as they made some 3.5-relevant changes.
Checked the cartopy version, it is the latest. Removed the map and cartopy imports. So I only plot a 400x300 grid.
Program doesn't hang anymore, but when I try to use zooming function from the toolbar the zooming window in the plot is very slow and lags with mouse movement. This is not happening in mpl 3.3.3 version. Can there be a memory problem?
I can confirm this with only Matplotlib. There is definitely a massive increase in memory usage (without cursor data it is ~300MB, so 4-5x increase).
It bisects to implementing the cursor data: #19908
So, it seems like a better implementation specific to QuadMesh should be considered here because it is a pretty bad interaction with large data right now.
From skimming #19908 nothing obviously jumps out at me as the problem. My knee-jerk guess is that the problem is actually in obj.contains, but the cursor data is just bringing that issue to the front by putting it in a hot loop.
I did some more investigation. I added some 'event' code to print mouse coordinates when moving over the figure.
When the matplotlib toolbar is active the program still hangs itself. But when the toolbar is deactivated, the program continues printing the mouse coordinates and there don't seem to be a problem with pcolormesh().
import numpy as np
import matplotlib.pyplot as plt
import cartopy.io.img_tiles as cimgt
import cartopy.crs as ccrs
import matplotlib as mpl
from matplotlib.backend_bases import Event
fig, ax1 = plt.subplots(num="TestXX", figsize=(30/2.54, 25/2.54))
def onmove(event):
x1,y1 = event.xdata, event.ydata
print(x1,y1)
cn1=ax1.figure.canvas.mpl_connect\
('motion_notify_event', onmove)
mpl.rcParams['toolbar'] = 'None'
#add a map of the Netherlands
request = cimgt.OSM()
ax1=plt.axes(projection=request.crs)
ax1.set_extent([3.25, 7.35, 50.45, 53.55],ccrs.PlateCarree())
ax1.add_image(request, 10)
#create a rectangular grid a little smaller than the map
#with cell sizes of 0.01 degree filled with random values
nx=400
ny=300
xedges=np.linspace(3.3, 7.3, nx)
yedges=np.linspace(50.5, 53.5, ny)
zvalues=np.random.random((nx, ny))
ax1.pcolormesh(xedges, yedges, zvalues.T ,\
alpha=0.3, shading='auto',transform=ccrs.PlateCarree())
plt.show()
This occurs because Collection.contains calls _prepare_points, which repeatedly tries to apply the (nonlinear, slow) transform to all paths. This would indeed be necessary if pickradius > 0 (in which case we need to know the paths in screen space to detect points within a certain number of pixels of a path, but in the case of get_cursor_data, we actually use pickradius=0, i.e. we treat the individual paths as filled and just want to know which paths contain the selected point. For that case, it is likely much more efficient to inverse-transform the query point and look for containment in data space? (Alternatively, we could add some kind of caching to _prepare points, but caching is well known to be one of the two hard problems in CS :-))
Below is a MPL only example. To demonstrate how slow things become uncomment the plt.show() comment. It seems like the time is not spent doing the transform, but in creating an individual Path object for every cell:
This suggests that doing an inverse transform on the mouse event display coordinates will not fix the issue, as we will still need to create a path for every cell to determine which cell the event falls into.
Given this issue prevents use of not-very-large (>~ 400x400 for me) QuadMeshes, I would propose that we disable get_cursor_data as a fairly urgent fix for 3.5.2. We could disable it by default for now, with an option to turn it on that has a performance warning in the docs?
importnumpyasnpimportmatplotlib.pyplotaspltfrommatplotlib.backend_basesimportMouseEventfig, ax1=plt.subplots()
# create a rectangular grid a little smaller than the map# with cell sizes of 0.01 degree filled with random valuesnx=400ny=400xedges=np.linspace(3.3, 7.3, nx)
yedges=np.linspace(50.5, 53.5, ny)
zvalues=np.random.random((nx, ny))
im=ax1.pcolormesh(xedges, yedges, zvalues.T,
alpha=0.3, shading='auto')
# plt.show()frompyinstrumentimportProfilerprofiler=Profiler()
profiler.start()
im.contains(MouseEvent('', fig.canvas, 100, 100))
profiler.stop()
profiler.print(show_all=True)
changed the title [-][Bug]: Plotting with matplotlib 3.5.0/cartopy: Program hangs no error message[/-][+][Bug]: pcolormesh is not responsive in Matplotlib 3.5[/+]on Jan 18, 2022
Activity
jklymak commented on Dec 10, 2021
Please try
3.5 changed the behaviour of pcolormesh when nx and ny were the same size; we used to drop the last row and column. Now we don't do that. In order to get the old behaviour you can drop the last row and column yourself, and user interp='flat'. However, I don't know how that interacts with cartopy.
Hermeij commented on Dec 10, 2021
Thnx. Tried your suggestion, but no succes, same behaviour. I think you meant shading='flat' instead of interp='flat'?
jklymak commented on Dec 10, 2021
Does the same behaviour happen without cartopy? Perhaps this is a cartopy problem, despite being linked to a Matplotlib version change. You may also make sure you are using the latest cartopy as they made some 3.5-relevant changes.
Hermeij commented on Dec 10, 2021
Checked the cartopy version, it is the latest. Removed the map and cartopy imports. So I only plot a 400x300 grid.
Program doesn't hang anymore, but when I try to use zooming function from the toolbar the zooming window in the plot is very slow and lags with mouse movement. This is not happening in mpl 3.3.3 version. Can there be a memory problem?
greglucas commented on Dec 10, 2021
I can confirm this with only Matplotlib. There is definitely a massive increase in memory usage (without cursor data it is ~300MB, so 4-5x increase).

It bisects to implementing the cursor data: #19908
So, it seems like a better implementation specific to QuadMesh should be considered here because it is a pretty bad interaction with large data right now.
tacaswell commented on Dec 13, 2021
From skimming #19908 nothing obviously jumps out at me as the problem. My knee-jerk guess is that the problem is actually in obj.contains, but the cursor data is just bringing that issue to the front by putting it in a hot loop.
Hermeij commented on Dec 13, 2021
I did some more investigation. I added some 'event' code to print mouse coordinates when moving over the figure.
When the matplotlib toolbar is active the program still hangs itself. But when the toolbar is deactivated, the program continues printing the mouse coordinates and there don't seem to be a problem with pcolormesh().
import numpy as np import matplotlib.pyplot as plt import cartopy.io.img_tiles as cimgt import cartopy.crs as ccrs import matplotlib as mpl from matplotlib.backend_bases import Event fig, ax1 = plt.subplots(num="TestXX", figsize=(30/2.54, 25/2.54)) def onmove(event): x1,y1 = event.xdata, event.ydata print(x1,y1) cn1=ax1.figure.canvas.mpl_connect\ ('motion_notify_event', onmove) mpl.rcParams['toolbar'] = 'None' #add a map of the Netherlands request = cimgt.OSM() ax1=plt.axes(projection=request.crs) ax1.set_extent([3.25, 7.35, 50.45, 53.55],ccrs.PlateCarree()) ax1.add_image(request, 10) #create a rectangular grid a little smaller than the map #with cell sizes of 0.01 degree filled with random values nx=400 ny=300 xedges=np.linspace(3.3, 7.3, nx) yedges=np.linspace(50.5, 53.5, ny) zvalues=np.random.random((nx, ny)) ax1.pcolormesh(xedges, yedges, zvalues.T ,\ alpha=0.3, shading='auto',transform=ccrs.PlateCarree()) plt.show()anntzer commented on Dec 13, 2021
This occurs because Collection.contains calls _prepare_points, which repeatedly tries to apply the (nonlinear, slow) transform to all paths. This would indeed be necessary if pickradius > 0 (in which case we need to know the paths in screen space to detect points within a certain number of pixels of a path, but in the case of get_cursor_data, we actually use pickradius=0, i.e. we treat the individual paths as filled and just want to know which paths contain the selected point. For that case, it is likely much more efficient to inverse-transform the query point and look for containment in data space? (Alternatively, we could add some kind of caching to _prepare points, but caching is well known to be one of the two hard problems in CS :-))
dstansby commented on Dec 28, 2021
Below is a MPL only example. To demonstrate how slow things become uncomment the plt.show() comment. It seems like the time is not spent doing the transform, but in creating an individual Path object for every cell:
This suggests that doing an inverse transform on the mouse event display coordinates will not fix the issue, as we will still need to create a path for every cell to determine which cell the event falls into.
Given this issue prevents use of not-very-large (>~ 400x400 for me) QuadMeshes, I would propose that we disable get_cursor_data as a fairly urgent fix for 3.5.2. We could disable it by default for now, with an option to turn it on that has a performance warning in the docs?
dstansby commented on Jan 18, 2022
I've just updated the title so this is easier to find for users with similar issues.