Я пытаюсь сохранить basemap object
для повторного использования, используя pickle
.
У меня есть более 100 фигур для создания, и basemap
занимает много времени.
Я следил за предыдущими постами по аналогичной проблеме, Сохранение фигур Matplotlib с использованием Pickle и Pickle: сохранение + загрузка рисунков MPL в файл .
Но всякий раз, когда я пробую свой собственный код, после сохранения pickle obj
я получаю RuntimeError: Can not put single artist in more than one figure
.
Ниже приведен фрагмент моей функции построения графиков
import os
import time
from dateutil.parser import parse
import numpy as np
import pickle
class ImageSaving:
__slots__ = 'label', 'unit', 'clim', 'cmp', 'data', 'date', 'filename', 'lat', 'log10', \
'lon', 'nColours', 'var', 'title', 'contour', 'points', 'levels', 'cbar'
def __init__(self, filename: str, var: str, data: np.ma.array = None, lon: np.array = None, cbar: bool = False,
lat: np.array = None, log10: bool = False, title: str = None, date=None, cmp: str = None,
clim: list = None, label: str = None, contour: bool = True, points: np.array = None):
for slot in self.__slots__:
setattr(self, slot, None)
self.log10 = log10
self.data = data
self.lon = lon
self.lat = lat
self.date = date
self.cmp = cmp
self.cbar = cbar
self.clim = clim
self.label = label
self.contour = contour
self.points = points
self.var = var
self.title = title
self.filename = filename
def _colourbar_sets(self):
if self.cmp is None:
self.cmp = 'jet'
self.nColours = 150
if 'temp' in self.var.lower():
# Colour bar limits and number of colours in the cb
if self.clim is None:
self.clim = -2, 30
# cb labels
if self.label is None:
self.label = 'Temperature'
self.unit = u'\u00B0C'
# cb tick labels
elif 'chl' in self.var:
# Colour bar limits and number of colours in the cb
if self.clim is None:
self.clim = 0.09, 100
# cb labels
if self.label is None:
self.label = 'Chlorophyll-{} Concentration'.format(r'$\it{a}$')
self.unit = 'mg m$^{-3}$'
elif 'sal' in self.var:
# Colour bar limits and number of colours in the cb
if self.clim is None:
self.clim = 25, 34
# cb labels
if self.label is None:
self.label = 'Salinity'
self.unit = 'psu'
def save_fig(self):
import matplotlib
matplotlib.use('Agg')
from matplotlib import (pyplot as plot, colors)
from mpl_toolkits import basemap as bsm
from mpl_toolkits.axes_grid1 import make_axes_locatable
self._colourbar_sets()
cb_location, cb_orientation = "right", 'vertical'
y0, y1 = 36.7, 37.12
x0, x1 = 136.9, 137.55
font_size, lw, pad = 20, 1, 0.05
lw *= 4
pad *= 3
font_size *= 3
plot.rcParams.update({'font.size': font_size + 4})
plot.rcParams['axes.linewidth'] = lw
plot.rcParams['xtick.major.size'] = 8
plot.rcParams['xtick.major.width'] = lw
plot.rcParams['ytick.major.size'] = 8
plot.rcParams['ytick.major.width'] = lw
plot.rcParams['axes.titlepad'] = 20
# fig = plot.figure(figsize=(26, 25), dpi=50)
fig, ax = plot.subplots(1, 1, figsize=(26, 25), dpi=50)
t = time.process_time()
basename = os.path.basename(self.filename)
lat_ts = round((self.lat.min() + self.lat.max()) / 2)
parallels = np.arange(np.floor(y0), np.ceil(y1) + 1, 1)
meridians = np.arange(np.floor(x0), np.ceil(x1), 1)
bsm_pickle = 'bsm_pickle'
if os.path.isfile(bsm_pickle):
with open(bsm_pickle, 'rb') as bmp:
m = pickle.load(bmp)
m.ax = ax
m.drawcoastlines(linewidth=1, ax=ax)
m.drawparallels(parallels, labels=[True, False, False, False], fontsize=font_size, linewidth=lw)
m.drawmeridians(meridians, labels=[0, 0, 0, 1], fontsize=font_size, linewidth=lw)
m.fillcontinents() # Fill the continents
m.drawmapboundary(fill_color='black', linewidth=lw) # Fill the globe with a blue color
else:
m = bsm.Basemap(projection='merc', lat_ts=lat_ts, resolution='f', llcrnrlat=y0,
urcrnrlat=y1, llcrnrlon=x0, urcrnrlon=x1, ax=ax)
m.drawparallels(parallels, labels=[True, False, False, False], fontsize=font_size, linewidth=lw)
m.drawmeridians(meridians, labels=[0, 0, 0, 1], fontsize=font_size, linewidth=lw)
m.fillcontinents() # Fill the continents
m.drawmapboundary(fill_color='black', linewidth=lw) # Fill the globe with a blue color
m.drawcoastlines(linewidth=1)
if os.path.isfile(bsm_pickle) is False:
with open(bsm_pickle, 'wb') as bmp:
pickle.dump(m, bmp)
if len(self.lon.shape) == 1:
lon2d, lat2d = np.meshgrid(self.lon, self.lat)
xx, yy = m(lon2d, lat2d)
else:
xx, yy = m(self.lon, self.lat)
cmn, cmx = self.clim
cmp = self.cmp
if self.cmp.endswith('.dat'):
chl_cmp_dict, _ = custom_cmap(self.cmp)
cmp = 'custom_cmp'
cmap = colors.LinearSegmentedColormap(cmp, chl_cmp_dict)
plot.register_cmap(cmap=cmap)
n_colors = self.nColours
bounds = np.linspace(cmn, cmx, self.nColours)
if self.log10 is True:
nrm = colors.LogNorm(*self.clim)
else:
nrm = colors.BoundaryNorm(boundaries=bounds, ncolors=self.nColours)
# ////////////////////////////////////// Map data onto image \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
if self.data.shape[0] == 1:
self.data = np.squeeze(self.data, axis=0)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
mesh = m.pcolormesh(xx, yy, self.data, shading='flat', norm=nrm, cmap=plot.get_cmap(cmp, n_colors))
# add contours
if self.contour is True:
levels = np.linspace(cmn, cmx, 5)
if self.levels is not None:
levels = self.levels
cnt = m.contour(xx, yy, self.data, levels=levels, colors='k', linewidths=lw)
if self.levels is None:
cnt.clabel(inline=True, fmt='%.2f', fontsize=35)
else:
cnt.clabel(inline=True, fontsize=35)
if self.points is not None:
px, py = m(self.points[:,0], self.points[:,1])
m.scatter(px, py, s=100, c='w')
# markerfacecoloralt='black', fillstyle='full', linestyle=None)
# Set visualization range
mesh.set_clim(vmin=cmn, vmax=cmx)
if self.cbar is True:
divider = make_axes_locatable(plot.gca())
cax = divider.append_axes(cb_location, size="3%", pad=pad)
cbr = plot.colorbar(mesh, cax=cax, orientation=cb_orientation, format='%.2f')
cb_label, cb_label_unit = self.label, self.unit
cbr.ax.tick_params(labelsize=font_size)
if (self.label is not None) and (cb_label_unit is None):
cbr.set_label("%s" % cb_label, size=font_size)
if cb_label_unit is not None:
cbr.set_label("%s [%s]" % (cb_label, cb_label_unit), size=font_size)
# Put title
if self.title is not None:
plot.title(self.title, fontsize=font_size)
elif self.date is not None:
plot.title(self.date.strftime('%d %b %Y'), fontsize=font_size)
else:
# Put title
if self.title is not None:
plot.title(self.title, fontsize=font_size)
elif self.date is not None:
lab = "%s [%s]" % (self.label, self.unit)
plot.title(f"{self.date.strftime('%d %b %Y')}\n{lab}", fontsize=font_size)
plot.tight_layout()
# File save
fig.savefig(self.filename, dpi=100, bbox_inches='tight')
plot.clf()
elapsed_time = time.process_time() - t
mnt, sec = divmod(elapsed_time, 60)
_, mnt = divmod(mnt, 60)
space = 45 - len(f'ImageSaved: {basename}')
print(f'ImageSaved: {basename} {"." * space} {int(mnt)} minutes{sec:7.3f} seconds')
plot.close()
. И обратная трассировка
c:\users\eligio\appdata\local\programs\python\python37\lib\site-packages\mpl_toolkits\basemap\__init__.py in drawcoastlines(self, linewidth, linestyle, color, antialiased, ax, zorder)
1855 self.set_axes_limits(ax=ax)
1856 # clip to map limbs
-> 1857 coastlines,c = self._cliplimb(ax,coastlines)
1858 return coastlines
1859
c:\users\eligio\appdata\local\programs\python\python37\lib\site-packages\mpl_toolkits\basemap\__init__.py in _cliplimb(self, ax, coll)
1810 c = self._mapboundarydrawn
1811 if c not in ax.patches:
-> 1812 p = ax.add_patch(c)
1813 #p.set_clip_on(False)
1814 try:
~\AppData\Roaming\Python\Python37\site-packages\matplotlib\axes\_base.py in add_patch(self, p)
1966 Add a `~.Patch` to the axes' patches; return the patch.
1967 """
-> 1968 self._set_artist_props(p)
1969 if p.get_clip_path() is None:
1970 p.set_clip_path(self.patch)
~\AppData\Roaming\Python\Python37\site-packages\matplotlib\axes\_base.py in _set_artist_props(self, a)
915 def _set_artist_props(self, a):
916 """set the boilerplate props for artists added to axes"""
--> 917 a.set_figure(self.figure)
918 if not a.is_transform_set():
919 a.set_transform(self.transData)
~\AppData\Roaming\Python\Python37\site-packages\matplotlib\artist.py in set_figure(self, fig)
688 # to more than one Axes
689 if self.figure is not None:
--> 690 raise RuntimeError("Can not put single artist in "
691 "more than one figure")
692 self.figure = fig
RuntimeError: Can not put single artist in more than one figure
Пример данных можно найти здесь . Первая строка, столбец - долгота, широта. Я получаю данные в numpy, как показано ниже
filename = 'temp_20040611_01z_data.txt'
data = []
with open(filename, 'rb') as txt:
for j, line in enumerate(txt.readlines()):
line = line.decode().strip().split()
data.append([np.single(col) for col in line])
data = np.array(data)
lon = data[0, 1:]
lat data[1:, 0]
data = data[1:, 1:]
И, наконец, вызываю функцию построения графиков следующим образом
f = filename.split('_')
ImageSaving(filename=filename,
clim=[data.min(), data.max()],
data=data, lon=lon, lat=lat, log10=False,
date=parse(f[1]), var=f[0].title()).save_fig()
Я понятия не имею, что мне следует сделать, чтобы это работало как это может у меня много времени. Любая помощь в решении этого вопроса высоко ценится. Заранее спасибо, что нашли время помочь.