Skip to content

Commit 7c9b9ec

Browse files
DingWBCopilot
andcommitted
Fix white-line bug in colorbar
Co-authored-by: Copilot <copilot@github.com>
1 parent c5dd0bf commit 7c9b9ec

5 files changed

Lines changed: 55 additions & 16 deletions

File tree

PyComplexHeatmap/clustermap.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,11 +139,16 @@ def _determine_cmap_params(self, plot_data, vmin, vmax, cmap, center, robust):
139139
vmin = np.nanpercentile(calc_data, 2)
140140
else:
141141
vmin = np.nanmin(calc_data)
142+
# Round to align colorbar QuadMesh boundary with the cax pixel
143+
# grid; avoids sub-pixel seams that PDF viewers render as white
144+
# lines (matplotlib issue #1188).
145+
vmin = round(float(vmin), 2)
142146
if vmax is None:
143147
if robust:
144148
vmax = np.nanpercentile(calc_data, 98)
145149
else:
146150
vmax = np.nanmax(calc_data)
151+
vmax = round(float(vmax), 2)
147152
self.vmin, self.vmax = vmin, vmax
148153

149154
# Choose default colormaps if not provided
@@ -361,7 +366,10 @@ def plot(
361366
# Possibly add a colorbar
362367
if self.cbar:
363368
cb = ax.figure.colorbar(mesh, cax, ax, **self.cbar_kws)
364-
cb.outline.set_linewidth(0)
369+
# cb.outline.set_linewidth(0)
370+
# Hide the sub-pixel seams between QuadMesh cells that PDF/SVG
371+
# viewers render as thin white lines (matplotlib issue #1188).
372+
cb.solids.set_edgecolor("face")
365373
# If rasterized is passed to pcolormesh, also rasterize the
366374
# colorbar to avoid white lines on the PDF rendering
367375
if kws.get("rasterized", False):
@@ -609,11 +617,16 @@ def plot_heatmap(
609617
vmin = np.nanpercentile(calc_data, 2)
610618
else:
611619
vmin = np.nanmin(calc_data)
620+
# Round to align colorbar QuadMesh boundary with the cax pixel grid;
621+
# avoids sub-pixel seams that PDF viewers render as white lines
622+
# (matplotlib issue #1188).
623+
vmin = round(float(vmin), 2)
612624
if vmax is None:
613625
if robust:
614626
vmax = np.nanpercentile(calc_data, 98)
615627
else:
616628
vmax = np.nanmax(calc_data)
629+
vmax = round(float(vmax), 2)
617630

618631
cmap=adjust_cmap(cmap,vmin=vmin,vmax=vmax,center=center,
619632
na_col=na_col)
@@ -2311,7 +2324,7 @@ def collect_legends(self):
23112324
self.legend_kws.setdefault("vmin", self.kwargs.get('vmin')) #round(vmin, 2))
23122325
self.legend_kws.setdefault("vmax", self.kwargs.get('vmax')) #round(vmax, 2))
23132326
self.legend_kws.setdefault("center", self.kwargs.get('center', None))
2314-
self.legend_kws.setdefault("extend", 'both')
2327+
self.legend_kws.setdefault("extend", 'both') # 'neither', 'both', 'min', 'max'
23152328
self.legend_kws.setdefault("extendfrac", 0.15)
23162329
cbar_height=self.legend_kws.pop('cbar_height',20)
23172330
self.legend_dict[self.label]=tuple([self.cmap, self.label, self.legend_kws, cbar_height, "cmap"])

PyComplexHeatmap/dotHeatmap.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -440,10 +440,13 @@ def format_data(self, data, mask=None, z_score=None, standard_scale=None):
440440
self.kwargs["marker"] = self.marker
441441

442442
if 'vmin' not in self.kwargs:
443-
self.vmin = np.nanmin(data2d.values)
443+
# Round to align the colorbar QuadMesh boundary with the cax pixel
444+
# grid; avoids sub-pixel seams that PDF viewers render as white
445+
# lines (matplotlib issue #1188).
446+
self.vmin = round(float(np.nanmin(data2d.values)), 2)
444447
self.kwargs.setdefault("vmin", self.vmin)
445448
if 'vmax' not in self.kwargs:
446-
self.vmax = np.nanmax(data2d.values)
449+
self.vmax = round(float(np.nanmax(data2d.values)), 2)
447450
self.kwargs.setdefault("vmax", self.vmax)
448451

449452
if z_score is not None and standard_scale is not None:
@@ -596,8 +599,10 @@ def collect_legends(self):
596599

597600
if isinstance(self.cmap, dict): #
598601
cmap_legend_kws = self.cmap_legend_kws.copy()
599-
cmap_legend_kws.setdefault("vmin", self.kwargs.get('vmin')) # round(vmin, 2))
600-
cmap_legend_kws.setdefault("vmax", self.kwargs.get('vmax')) # round(vmax, 2))
602+
_vmin = self.kwargs.get('vmin')
603+
_vmax = self.kwargs.get('vmax')
604+
cmap_legend_kws.setdefault("vmin", round(_vmin, 2) if _vmin is not None else _vmin)
605+
cmap_legend_kws.setdefault("vmax", round(_vmax, 2) if _vmax is not None else _vmax)
601606
cmap_legend_kws.setdefault("center", self.kwargs.get('center', None))
602607
cmap_legend_kws.setdefault("extend", 'both')
603608
cmap_legend_kws.setdefault("extendfrac", 0.15)
@@ -608,8 +613,10 @@ def collect_legends(self):
608613
cmap = self.cmap
609614
c = self.kwargs.get("c", None)
610615
cmap_legend_kws = self.cmap_legend_kws.copy()
611-
cmap_legend_kws.setdefault("vmin", self.kwargs.get('vmin')) # round(vmin, 2))
612-
cmap_legend_kws.setdefault("vmax", self.kwargs.get('vmax')) # round(vmax, 2))
616+
_vmin = self.kwargs.get('vmin')
617+
_vmax = self.kwargs.get('vmax')
618+
cmap_legend_kws.setdefault("vmin", round(_vmin, 2) if _vmin is not None else _vmin)
619+
cmap_legend_kws.setdefault("vmax", round(_vmax, 2) if _vmax is not None else _vmax)
613620
cmap_legend_kws.setdefault("center", self.kwargs.get('center', None))
614621
cmap_legend_kws.setdefault("extend", 'both')
615622
cmap_legend_kws.setdefault("extendfrac", 0.15)

PyComplexHeatmap/utils.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,7 @@ def plot_cmap_legend(
561561
cbar_kws.setdefault("fraction", 1)
562562
cbar_kws.setdefault("shrink", 1)
563563
cbar_kws.setdefault("pad", 0)
564-
cbar_kws.setdefault("extend", 'both')
564+
cbar_kws.setdefault("extend", 'both') # 'neither', 'both', 'min', 'max'
565565
cbar_kws.setdefault("extendfrac", 0.1)
566566
vmax = cbar_kws.pop("vmax", 1)
567567
vmin = cbar_kws.pop("vmin", 0)
@@ -582,7 +582,26 @@ def plot_cmap_legend(
582582
cax.yaxis.set_label_position(label_side)
583583
cax.yaxis.set_ticks_position(label_side)
584584
cbar = ax.figure.colorbar(m, cax=cax, **cbar_kws) # use_gridspec=True
585-
# cbar.set_ticks([vmin,center,vmax])
585+
# Fix white-Line bug in colorbar: Replace the colorbar's QuadMesh body with a single imshow gradient.
586+
# matplotlib issue #1188: PDF/SVG viewers render sub-pixel seams between
587+
# QuadMesh cells (and between the QuadMesh and the cax border) as thin
588+
# white lines. A single imshow image has no internal seams and fills
589+
# the cax exactly, so the colorbar body renders cleanly. Outline,
590+
# ticks, label and dividers remain vector graphics.
591+
cbar.solids.set_visible(False)
592+
gradient = np.linspace(vmin, vmax, 1024).reshape(-1, 1)
593+
cax.imshow(
594+
gradient,
595+
aspect="auto",
596+
cmap=m.cmap,
597+
norm=m.norm,
598+
origin="lower",
599+
extent=(0, 1, vmin, vmax),
600+
interpolation="bilinear",
601+
zorder=cbar.solids.get_zorder(),
602+
)
603+
cax.set_xlim(0, 1)
604+
cax.set_ylim(vmin, vmax)
586605
# cbar.outline.set_color('white')
587606
# cbar.outline.set_linewidth(2)
588607
# cbar.dividers.set_color('red')

notebooks/composite_heatmaps.ipynb

Lines changed: 3 additions & 3 deletions
Large diffs are not rendered by default.

notebooks/cpg_modules.ipynb

Lines changed: 3 additions & 3 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)