From a5b1cdc945ef4c66e60f4a72a1fbc635261a9738 Mon Sep 17 00:00:00 2001 From: "Claude Opus 4.8" Date: Wed, 29 Jul 2026 15:43:14 +0200 Subject: [PATCH] Fix matplotlib>=3.9 compatibility: replace removed plt.cm.get_cmap matplotlib deprecated `matplotlib.cm.get_cmap` in 3.7 and removed it in 3.9, so on newer matplotlib (reproduced on 3.11) any colormap export raised: AttributeError: module 'matplotlib.cm' has no attribute 'get_cmap' This broke `python -m pingmapper test` during sonogram tile export (sonObj._colorize_array_batched) and would also break the rectification colormap path (rectObj) and the substrate summary plot. Replace every `plt.cm.get_cmap(...)` call with the still-supported `plt.get_cmap(...)` (matplotlib.pyplot.get_cmap), a drop-in that also handles a None colormap name like the old API. The already-valid `matplotlib.colormaps.get_cmap(...)` calls are left unchanged. Verified by running `python -m pingmapper test` (small dataset) through the full read -> rectify -> substrate pipeline to completion on matplotlib 3.11. Fixes #203. Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com> --- pingmapper/class_mapSubstrateObj.py | 2 +- pingmapper/class_rectObj.py | 10 +++++----- pingmapper/class_sonObj.py | 2 +- pingmapper/class_sonObj_nadirgaptest.py | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pingmapper/class_mapSubstrateObj.py b/pingmapper/class_mapSubstrateObj.py index 5f60d4e..536d519 100644 --- a/pingmapper/class_mapSubstrateObj.py +++ b/pingmapper/class_mapSubstrateObj.py @@ -880,7 +880,7 @@ def _pltSubClass(self, map_class_method, chunk, npz, spdCor=1, maxCrop=0, probs= ax.imshow(son, cmap='gray') # Prepare color map - color_map = plt.cm.get_cmap('viridis') + color_map = plt.get_cmap('viridis') im = ax.imshow(c, cmap=color_map, alpha=0.5, vmin=minSoft, vmax=maxSoft) ax.axis('off') diff --git a/pingmapper/class_rectObj.py b/pingmapper/class_rectObj.py index c6ecb8f..db901d0 100644 --- a/pingmapper/class_rectObj.py +++ b/pingmapper/class_rectObj.py @@ -1968,7 +1968,7 @@ def _rectSonHeading(self, df: pd.DataFrame, chunk, son=True, wgs=False, interpol use_uint8_rgb = self._export_colormap_as_uint8() if source_scale_bounds is not None: norm_data = self._normalize_with_bounds(sonRect_raw16, source_scale_bounds[0], source_scale_bounds[1]) - cmap = plt.cm.get_cmap(self.son_colorMap_name)(norm_data) + cmap = plt.get_cmap(self.son_colorMap_name)(norm_data) if use_uint8_rgb: sonRect_out = np.clip(cmap[:, :, :3] * 255.0, 0, 255).astype(np.uint8) else: @@ -2769,9 +2769,9 @@ def _rectSonRubber(self, if source_scale_bounds is not None: norm_data = self._normalize_with_bounds(out16_raw, source_scale_bounds[0], source_scale_bounds[1]) if use_uint8_rgb: - out16 = np.clip(plt.cm.get_cmap(self.son_colorMap_name)(norm_data)[:, :, :3] * 255.0, 0, 255).astype(np.uint8) + out16 = np.clip(plt.get_cmap(self.son_colorMap_name)(norm_data)[:, :, :3] * 255.0, 0, 255).astype(np.uint8) else: - out16 = np.clip(plt.cm.get_cmap(self.son_colorMap_name)(norm_data)[:, :, :3] * 65535.0, 0, 65535).astype(np.uint16) + out16 = np.clip(plt.get_cmap(self.son_colorMap_name)(norm_data)[:, :, :3] * 65535.0, 0, 65535).astype(np.uint16) valid_mask = out16_raw > 0 out16[valid_mask] = np.maximum(out16[valid_mask], 1) else: @@ -2914,9 +2914,9 @@ def _rectSonRubber(self, if source_scale_bounds is not None: norm_data = self._normalize_with_bounds(out16_raw, source_scale_bounds[0], source_scale_bounds[1]) if use_uint8_rgb: - out16 = np.clip(plt.cm.get_cmap(self.son_colorMap_name)(norm_data)[:, :, :3] * 255.0, 0, 255).astype(np.uint8) + out16 = np.clip(plt.get_cmap(self.son_colorMap_name)(norm_data)[:, :, :3] * 255.0, 0, 255).astype(np.uint8) else: - out16 = np.clip(plt.cm.get_cmap(self.son_colorMap_name)(norm_data)[:, :, :3] * 65535.0, 0, 65535).astype(np.uint16) + out16 = np.clip(plt.get_cmap(self.son_colorMap_name)(norm_data)[:, :, :3] * 65535.0, 0, 65535).astype(np.uint16) valid_mask = out16_raw > 0 out16[valid_mask] = np.maximum(out16[valid_mask], 1) else: diff --git a/pingmapper/class_sonObj.py b/pingmapper/class_sonObj.py index be6c495..fcae3e3 100644 --- a/pingmapper/class_sonObj.py +++ b/pingmapper/class_sonObj.py @@ -1872,7 +1872,7 @@ def _prepare_export_uint16_display(self, data): return out def _colorize_array_batched(self, norm_data, valid_mask, cmap_name, scale_max, out_dtype): - cmap = plt.cm.get_cmap(cmap_name) + cmap = plt.get_cmap(cmap_name) height, width = norm_data.shape out = np.zeros((height, width, 3), dtype=out_dtype) diff --git a/pingmapper/class_sonObj_nadirgaptest.py b/pingmapper/class_sonObj_nadirgaptest.py index 5cd6c01..c7005e5 100644 --- a/pingmapper/class_sonObj_nadirgaptest.py +++ b/pingmapper/class_sonObj_nadirgaptest.py @@ -1500,7 +1500,7 @@ def _writeTilesPlot(self, # plt.savefig(outfile) norm_data = data / 255.0 - colored_data = plt.cm.get_cmap(self.sonogram_colorMap)(norm_data) + colored_data = plt.get_cmap(self.sonogram_colorMap)(norm_data) colored_data = (colored_data[:, :, :3] * 255).astype('uint8') data = colored_data