I'm fairly new to matplotlib and python in general and what I'm trying to do is rather basic. However, even after quite some googling time I cannot find a solution for this:
Here is the problem:
I'd like to draw a circle with different color of the border and the face, i.e. set edgecolor
and facecolor
differently. I'd also like to have an alpha-channel, i.e. alpha=0.5
. Now while all of this works fine, the resulting circle does not have a single border color, but 2 borders are drawn. One, the outer, in the color I specified for edgecolor
and the other in a color I assume to be a combination between the edgecolor and the facecolor.
Here is my code:
from matplotlib import pyplot as plt
point = (1.0, 1.0)
c = plt.Circle(point, 1, facecolor='green', edgecolor='orange', linewidth=15.0, alpha=0.5)
fig, ax = plt.subplots()
ax.add_artist(c)
plt.show()
And here is an illustration:
Ok, this might be a minor thing, but this 2nd border drives me nuts!
Am I doing something wrong? Is that just the way it is? Any help would be much appreciated.
To some extent, yes, this is just the way it is.
The point is that the edge is literally drawn on the edge of the circle.
This means that half of the edge width is drawn on top of the face of the circle and the other half outside. If now you set alpha<1.0
you see, as you concluded correctly, an overlap of the facecolor and the edgecolor.
However, you can get rid of that 'extra border'. Below are 2 ways how to do so which one works best for you depends on what exactly you want to do.
1st Suggestion:
The simplest, IMHO, is to only set alpha for the facecolor
. This can be done by setting the alpha channel for the facecolor
directly and omit the alpha
argument in the call of Circle
. You can set the alpha-channel with the colorConverter
:
from matplotlib.colors import colorConverter
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(8, 8))
ax.axis('equal')
ax.set_xlim(0,1)
ax.set_ylim(0,1)
# Here you set alpha for the faceolor
fc = colorConverter.to_rgba('green', alpha=0.5)
point = (1.0, 1.0)
r = 1.0
lw = 15.0
ec = 'orange'
# NOTE: do not set alpha when calling Circle!
c = plt.Circle(point, r,fc=fc, ec=ec, lw=lw)
ax.add_artist(c)
plt.show()
2nd Suggestion
A more elaborated option is to 'wipe' the edge of the circle with a white edge after only plotting the face, then only plot the edge. With this approach both colours appear with the alpha channel. But note that also in this case any object that lies 'under' the edge will be completely masked by the edge:
import matplotlib.pyplot as plt
from matplotlib.collections import PatchCollection
point = (1.0, 1.0)
r = 1.0
alpha = 0.5
lw = 15.0
fc = 'green'
ec = 'orange'
# First only draw the facecolor
c_face = plt.Circle(point, r, alpha=alpha, fc=fc, lw=0.0)
# Draw a non-transparent white edge to wipe the facecolor where they overlap
c_wipe = plt.Circle(point, r, alpha=1.0, ec='white', fc='none', lw=lw)
# Now draw only the edge
c_edge = plt.Circle(point, r, alpha=alpha, fc='none', ec=ec, lw=lw)
circle_patch = PatchCollection([c_face, c_wipe, c_edge], match_original=True)
fig, ax = plt.subplots(figsize=(8, 8))
ax.axis('equal')
ax.set_xlim(0,1)
ax.set_ylim(0,1)
ax.add_artist(circle_patch)
plt.show()
Here is a gist that deals with this issue following the 2nd suggestion. Simply download the mod_patch.py file and off you go.
Here is how it can be used:
import matplotlib.pyplot as plt
from mod_patch import get_Patch
fig, ax = plt.subplots(figsize=(8,8))
c = get_Patch(plt.Circle, (0,0.5), 0.5, facecolor='green', edgecolor='orange', alpha=0.5, lw=15)
ax.add_artist(c)
r = get_Patch(plt.Rectangle, (0.5,0), 0.5, 0.5, facecolor='green', edgecolor='orange', alpha=0.5, lw=15)
ax.add_artist(r)
plt.show()
For completeness, here the definition of get_Patch
:
from matplotlib.collections import PatchCollection
def get_Patch(a_Patch, *args, **kwargs):
background_color = kwargs.pop(
'bgc',
kwargs.pop('background_color', 'white')
)
alpha = kwargs.get('alpha', 1.0)
patches = []
lw = kwargs.get('lw', kwargs.get('linewidth', 0.0))
if alpha < 1.0 and lw:
color = kwargs.get('c', kwargs.get('color', None))
fc = kwargs.get('facecolor', kwargs.get('fc', None))
ec = kwargs.get('edgecolor', kwargs.get('ec', None))
face_kwargs = dict(kwargs)
face_kwargs['fc'] = fc if fc is not None else color
face_kwargs['lw'] = 0.0
p_face = a_Patch(*args, **face_kwargs)
patches.append(p_face)
wipe_kwargs = dict(kwargs)
wipe_kwargs['fc'] = 'none'
wipe_kwargs['ec'] = background_color
wipe_kwargs['alpha'] = 1.0
p_wipe = a_Patch(*args, **wipe_kwargs)
patches.append(p_wipe)
edge_kwargs = dict(kwargs)
edge_kwargs['fc'] = 'none'
edge_kwargs['ec'] = ec if ec is not None else color
p_edge = a_Patch(*args, **edge_kwargs)
patches.append(p_edge)
else:
p_simple = a_Patch(*args, **kwargs)
patches.append(p_simple)
return PatchCollection(patches, match_original=True)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With