'How to remove gaps between adjacent matplotlib patches

I am trying to plot a collection of finite element data with colored patches used to represent the value in each element.

Unfortunately, when I plot the patches I get small gaps between adjacent patches.

I have created an example script below to describe the problem.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.collections import PatchCollection

nodes = [[0.1, 0.1],
        [0.25, 0.1],
        [0.75, 0.9],
        [0.1, 0.9],
        [0.9, 0.1],
        [0.9, 0.9]]
        
elems =[[0,1, 2, 3],
        [1, 4, 5, 2]]

patches = []
for elem in elems:
    xy = np.array([nodes[i] for i in elem])
    elem = mpatches.Polygon(xy, closed=True, snap=False, antialiased=True)
    patches.append(elem)

col = PatchCollection(patches)

fig, ax = plt.subplots(1, 1)

ax.add_collection(col)
plt.show()

Which produces the following plot with the unwanted gap. Adjusting the shape of the plotting window changes the appearance of the gap.

At the moment my workaround is to set col.set_edgecolor("face")` and to include a small edge width. However, it's hard to set the edge to reduce artifacts when there is a range of different patch sizes.

Edit: as @tdy pointed out increasing the dpi when saving the figure helps reduce the gap, but doesn't eliminate it entirely. With an excessive dpi of 1000 the gap is only visible if you zoom in (click the image first) on a high res monitor. enter image description here

For example for my real data with no edge width (edgecolor=None) there are lines separating each element. enter image description here

Setting edgecolor="face" with linewidth=1.0 removes the lines between elements, but adds small artifacts at the corners of cells. This are particularly visible at the front of the geometry where the colors are very different and the elements are small. enter image description here enter image description here

Reducing the linewidth to 0.5 reduces the impact of these artifacts, but the lines between the larger elements in the middle start to reappear. enter image description here enter image description here

Is it possible to fix these gaps? I know this is a small thing, but I'd love to have a way to plot my data in python without artifacts.

I have tried setting snap=False and antialisased=True, but neither had any effect.



Solution 1:[1]

While setting the color of the whole collection does remove the gap as @rvd suggested, this limits us to 1 color for all polygons:

col = PatchCollection(patches, color='C0') # limit 1 color per collection

A more robust solution is to set the color of each polygon and enable match_original in the collection:

match_original : If True, use the colors and linewidths of the original patches.

This allows us to have distinct colors per patch instead of 1 global color per collection:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.collections import PatchCollection

# use numpy arrays to simplify indexing
nodes = np.array([[0.1, 0.1], [0.25, 0.1], [0.75, 0.9], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]])
elems = np.array([[0, 1, 2, 3], [1, 4, 5, 2]])
colors = np.array(['C0', 'C4']) # color per elem

# set color per polygon
patches = [mpatches.Polygon(nodes[e], color=c) for e, c in zip(elems, colors)]
#                                     -------

# retain original polygon colors
collection = PatchCollection(patches, match_original=True)
#                                     -------------------

fig, ax = plt.subplots()
ax.add_collection(collection)

plt.show()

Of course if you only want one color, you can remove the zip and just set a single color (as long as match_original is enabled in the collection):

patches = [mpatches.Polygon(nodes[e], color='C0') for e in elems]
collection = PatchCollection(patches, match_original=True)


Update: As I can't reproduce the artifacts using the sample data, try using these extra options on your end:

  • rasterized=True and linewidth=0 in the collection
  • dpi=600 (or higher) when saving the figure
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.collections import PatchCollection

# use numpy arrays to simplify indexing
nodes = np.array([[0.1, 0.1], [0.25, 0.1], [0.75, 0.9], [0.1, 0.9], [0.9, 0.1], [0.9, 0.9]])
elems = np.array([[0, 1, 2, 3], [1, 4, 5, 2]])
colors = np.array(['C0', 'C4']) # color per elem

# set color per polygon
patches = [mpatches.Polygon(nodes[e], color=c) for e, c in zip(elems, colors)]
#                                     -------

# retain original polygon colors
collection = PatchCollection(patches, snap=True, match_original=True, rasterized=True, linewidth=0)
#                                                -------------------  ---------------  -----------

fig, ax = plt.subplots()
ax.add_collection(collection)

fig.savefig('fe.png', dpi=600)
#                     -------

Solution 2:[2]

You can fix this by using PatchCollection colors and different PatchCollections.

PatchCollection -> no lines, even without edgecolors and without linewidth=0

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from matplotlib.collections import PatchCollection

nodes = [[0.1, 0.1],
         [0.25, 0.1],
         [0.75, 0.9],
         [0.1, 0.9],
         [0.9, 0.1],
         [0.9, 0.9]]

elems = [[0, 1, 2, 3],
         [1, 4, 5, 2]]

collections = []
for elem in elems:
    xy = np.array([nodes[i] for i in elem], dtype=float)
    collections.append(PatchCollection([mpatches.Polygon(xy)], snap=True, color='black'))

fig, ax = plt.subplots(1, 1)
for col in collections:
    ax.add_collection(col)
plt.show()

enter image description here

I have no idea why this works.

Edit: Applied tdy suggestions from the comments.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2