How to plot a hollow circle with gradient/fadeout/glow in Matplotlib? - python

I would like to plot a circle of a given outer radius which would have an empty hole of a given inner radius. Then the resulting ring would have a fade-out gradient fill, however starting not from the center of the circle, but from the border of the inner circle. In Photoshop it's called "glow", from what I know. Is something like this possible?
here's an image showing what I mean

You could create an image from a function that is zero inside the circle and goes from 1 to 0 on the outside.
Using a colormap that goes from fully transparent white to opaque red would not only interpolate the color but also the transparency.
Here is an example, placing some text to demonstrate the effect of the transparency.
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
import numpy as np
inner_radius = 1
outer_radius = 3
center_x = 6
center_y = 4
halo_color = 'gold'
# center_color = 'none' # for an empty center
center_color = '#ff334466' ## redish with 25% alpha
xmin = center_x - outer_radius
xmax = center_x + outer_radius
ymin = center_y - outer_radius
ymax = center_y + outer_radius
x, y = np.meshgrid(np.linspace(xmin, xmax, 500), np.linspace(ymin, ymax, 500))
r = np.sqrt((x - center_x) ** 2 + (y - center_y) ** 2)
z = np.where(r < inner_radius, np.nan, np.clip(outer_radius - r, 0, np.inf))
cmap = LinearSegmentedColormap.from_list('', ['#FFFFFF00', halo_color])
cmap.set_bad(center_color)
plt.text(center_x, center_y, "Test", size=50, color='b')
plt.imshow(z, cmap=cmap, extent=[xmin, xmax, ymin, ymax], origin='lower', zorder=3)
plt.axis('equal')
plt.show()

Related

Problem with drawing an arc from the center of a circle using matplotlib

I'm having trouble centering an arc on a circle.
Can anyone suggest how to write the code so that the center of the arc is always in the center of the circle I drew?
I add the arc to the graph with the code below. The variable used to draw the arc is the angle alpha.
arc_angles = linspace(0, alpha, 20)
arc_xs = radius * cos(arc_angles)
arc_ys = radius * sin(arc_angles)
plt.plot(arc_xs, -arc_ys, color='red', lw=3)
plt.gca().annotate('Arc', xy=(-radius /2, -radius /2), xycoords='data', fontsize=12, rotation=180+math.degrees(alpha))
I add all the code below.
from numpy.lib.function_base import angle
import logging
import math
import matplotlib.pyplot as plt
import numpy as np
from numpy import sin, cos, pi, linspace
logger = logging.getLogger(__name__)
def draw_section(h=0.16, d=0.2):
if validate_filling(h, d):
radius = d/2
plt.plot(0, 0, color='black', marker='o')
plt.gca().annotate('O (0, 0)', xy=(0 + radius/10, 0 + radius/10), xycoords='data', fontsize=12)
plt.xlim(-radius - 0.05, radius + 0.05)
plt.ylim(-radius, radius + 0.05)
plt.gca().set_aspect('equal')
# draw circle
angels = linspace(0 * pi, 2 * pi, 100)
xs = radius * cos(angels)
ys = radius * sin(angels)
plt.plot(xs, ys, color='brown')
# draw diameter
plt.plot(radius, 0, marker='o', color='blue')
plt.plot(-radius, 0, marker='o', color='blue')
plt.plot([radius, -radius], [0, 0])
plt.gca().annotate(f"Diameter={d}", xy=(radius/8, -radius/5), xycoords='data', fontsize=12)
# draw level of water
plt.plot(0, -radius, marker='o', color='purple')
plt.plot(0, h - radius, marker='o', color='purple')
plt.plot([0, 0], [-radius, h - radius], color='purple')
plt.gca().annotate('Water lvl', xy=(-radius/5, -radius/1.5), xycoords='data', fontsize=12, rotation=90)
# Draw arc as created by water level
chord = math.sqrt((radius ** 2 - ((h-radius) ** 2))) * 2
# calculate angle
alpha = math.acos((radius ** 2 + radius ** 2 - chord ** 2) / (2 * radius ** 2))
# Create arc
arc_angles = linspace(0, alpha, 20)
arc_xs = radius * cos(arc_angles)
arc_ys = radius * sin(arc_angles)
plt.plot(arc_xs, -arc_ys, color='red', lw=3)
plt.gca().annotate('Arc', xy=(-radius /2, -radius /2), xycoords='data', fontsize=12, rotation=180+math.degrees(alpha))
plt.show()
else:
logger.info(f"h cannot be greater than d.")
draw_section(h=0.30, d=0.5)
This is output:
Code output
The effect I want to get is a graph with the possibility of changing the water height in the pipe cross-section. The height of the water table should be a chord, always a horizontal line.
Cross section of pipe - effect I want to get
As I wrote above, I would like to draw an arc in the center of the circle to draw a chord from the endpoints marked by the arc.
effect I want to get

Python: Drawing an area based on opposite geographic coordinates

I try to plot and connect points in in the shape of a rectangle, but I am doing something wrong.
I have vectors of coordinates like this:
x = [6.2372045620000005, 6.237194762000001, 6.237194762000001, 6.2372045620000005]
y = [51.071833453, 51.071835828999994, 51.071833453, 51.071835828999994]
First, I plot point data:
plt.scatter(x, y, color = 'blue')
Then, I try to add line between points in such a way, that a rectangle is formed. Unfortunately this below does not work correctly.
plt.scatter(x, y, color = 'blue')
plot.plot(x,y)
Do you know what I am doing wrong? It's a simple thing for sure, but I'm stuck with that..
Thanks for you help and comments.
Usually it is assumed that the coordinates, for any shape, are already ordered.
If you're confident that you have a rectangle (all right corners, not some quadrilateral) you can take the min/max values of your x and y coordinates to get the correct corner points. Given the coordinates that you already defined in your post:
import matplotlib.patches as patches
import matplotlib.pyplot as plt
xmin = min(x)
xmax = max(x)
xsize = xmax - xmin
ymin = min(y)
ymax = max(y)
ysize = ymax - ymin
Using Matplotlib, you can either use the Rectangle patch like:
fig, ax = plt.subplots(dpi=86)
ax.scatter(x, y, color='blue')
rect = patches.Rectangle(
(xmin, ymin), xsize, ysize,
lw=2, edgecolor='r', facecolor='none',
)
ax.add_patch(rect)
Or the Polygon patch, for any arbitrary shape. Both methods will work in this case:
points = [
(xmin, ymin),
(xmax, ymin),
(xmax, ymax),
(xmin, ymax),
]
poly = patches.Polygon(
points, lw=2, edgecolor='r', facecolor='#ff000011', closed=True,
)
ax.add_patch(poly)
If you do have something less regular shaped, like a quadrilateral, calculating the convex-hull might help getting the correct order of the coordinates. See for example:
Convex hull of 4 points

Python highlight user chosen area in contourf plot

What is the best solution for highlighting an area in a contourf plot?
I want the background to be opacity of 0.5 and the user chosen area to be normal. How can I achieve this?
In How to nicely plot clipped layered artists in matplotlib? Jake Vanderplas shows a way to draw a rectangle with a rectangular hole. The code can be adapted for your situation. The following example starts from a tutorial example, and highlights the third contour:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.path import Path
from matplotlib.patches import PathPatch
def DoubleRect(xy1, width1, height1,
xy2, width2, height2, **kwargs):
base = np.array([(0, 0), (0, 1), (1, 1), (1, 0), (0, 0)])
verts = np.vstack([xy1 + (width1, height1) * base,
xy2 + (width2, height2) * base[::-1],
xy1])
codes = 2 * ([Path.MOVETO] + 4 * [Path.LINETO]) + [Path.CLOSEPOLY]
return PathPatch(Path(verts, codes), **kwargs)
origin = 'lower'
delta = 0.025
x = y = np.arange(-3.0, 3.01, delta)
X, Y = np.meshgrid(x, y)
Z1 = np.exp(-X ** 2 - Y ** 2)
Z2 = np.exp(-(X - 1) ** 2 - (Y - 1) ** 2)
Z = (Z1 - Z2) * 2
fig, ax = plt.subplots()
contours = ax.contourf(X, Y, Z, 10, cmap=plt.cm.turbo, origin=origin)
# contours.collections[2].set_color('deepskyblue') # mark one contour
# calculate (or get) the coordinates of the hole
bbox = contours.collections[2].get_paths()[0].get_extents()
hole_xy, hole_width, hole_height = bbox.p0, bbox.width, bbox.height
# find the coordinates of the surrounding rectangle
xmin, xmax = ax.get_xlim()
ymin, ymax = ax.get_ylim()
full_rect = plt.Rectangle((xmin, ymin), xmax - xmin, ymax - ymin, color='black', alpha=0.5)
ax.add_patch(full_rect)
# create a rectangle with a hole to clip the surrounding rectangle
mask = DoubleRect((xmin, ymin), xmax - xmin, ymax - ymin,
hole_xy, hole_width, hole_height,
facecolor='none', edgecolor='none')
ax.add_patch(mask)
full_rect.set_clip_path(mask)
plt.show()
Instead of darkening the outside region, it could also be hatched (similar to the linked post). This would set the edge color of the mask to 'black', and create the full rectangle with hatching.
full_rect = plt.Rectangle((xmin, ymin), xmax - xmin, ymax - ymin, facecolor='none', edgecolor='black', hatch='//')

How to draw colored rectangles around grouped clusters in dendogram?

I try to add colored rectangle to dendrogram results like as follow:
this is my dendrogram codes:
from scipy.cluster.hierarchy import dendrogram
...
plt.figure(figsize=(250, 100))
labelsize=20
ticksize=15
plt.title(file_name.split(".")[0], fontsize=labelsize)
plt.xlabel('stock', fontsize=labelsize)
plt.ylabel('distance', fontsize=labelsize)
dendrogram(
Z,
leaf_rotation=90., # rotates the x axis labels
leaf_font_size=8., # font size for the x axis labels
labels = corr.columns
)
pylab.yticks(fontsize=ticksize)
pylab.xticks(rotation=-90, fontsize=ticksize)
However, this is only add colorful line not a rectangle like in the above image. How can I create image like this?
Thanks
You can loop through the generated path collections and draw a bounding box.
Optionally, you could set the height to the color_threshold= parameter, which defaults to Z[:, 2].max() * 0.7.
The last collection is are the unclassified lines, so the example code below loops through all earlier collections.
import matplotlib.pyplot as plt
from scipy.cluster import hierarchy
import numpy as np
N = 15
ytdist = np.random.randint(10, 1000, N * (N + 1) // 2)
Z = hierarchy.linkage(ytdist)
fig, ax = plt.subplots(1, 1, figsize=(8, 3))
dn1 = hierarchy.dendrogram(Z, ax=ax)
for coll in ax.collections[:-1]: # the last collection is the ungrouped level
xmin, xmax = np.inf, -np.inf
ymax = -np.inf
for p in coll.get_paths():
box = p.get_extents()
(x0, _), (x1, y1) = p.get_extents().get_points()
xmin = min(xmin, x0)
xmax = max(xmax, x1)
ymax = max(ymax, y1)
rec = plt.Rectangle((xmin - 4, 0), xmax - xmin + 8, ymax*1.05,
facecolor=coll.get_color()[0], alpha=0.2, edgecolor="none")
ax.add_patch(rec)
plt.show()

python matplotlib.patches: draw a Circle patch but keep only part of the circle

I am trying to plot a picture, and I have a rectangle plotted, then I wanted to plot a arc-shape element, but this element has to be precise, and it is only part of a circle that is outside the rectangle shape. So, I've tried to use Arc patch to create the same thing, but the shape doesn't match.
As a result, I am wondering if it is possible to plot the circle, but only keeps the part of it that is outside the rectangle? To be more specific, I want to discard/hide/get rid of the BLUE ARROW part in the image below, and keep the RED ARROW part, which is outside the rectangle like an Arc-shape. Is there any method to do so?
Here are my codes:
from matplotlib.patches import Circle, Rectangle, Arc, Ellipse
def plot_pic(ax=None, color='black', lw=2, scale = 15):
# get the current ax if ax is None
if ax is None:
ax = plt.gca()
# Plot the rectangle
rec = Rectangle((-(7.32 * scale / 2+ 5.5 * scale +11 * scale),0), width = (5.5 * scale * 2 + 11 * scale * 2 + 7.32 * scale), height = 16.5 * scale, linewidth = lw, color = color, fill = False)
testCircle = Circle((0, 11 * scale), radius = 9.15 * scale, color = color, lw = lw, fill = False)
# List of elements to be plotted
pic_elements = [rec, testCircle]
# Add the elements onto the axes
for element in pic_elements:
ax.add_patch(element)
return ax
After this, run the following:
plt.figure(figsize=(16, 22))
plt.xlim(-600,600)
plt.ylim(-100,1700)
plot_pic()
plt.show()
Thank you very much for your help.
If it's really just to do what you say, you could set the facecolor of the rectangle to white, and the zorder of the circle to 0 so it gets plotted behind:
def plot_pic(ax=None, color='black', lw=2, scale = 15):
# get the current ax if ax is None
if ax is None:
ax = plt.gca()
# Plot the rectangle
rec = Rectangle((-(7.32 * scale / 2+ 5.5 * scale +11 * scale),0), width = (5.5 * scale * 2 + 11 * scale * 2 + 7.32 * scale), height = 16.5 * scale, linewidth = lw, color = color, fc='white')
testCircle = Circle((0, 11 * scale), radius = 9.15 * scale, color = color, lw = lw, fill = False, zorder=0)
# List of elements to be plotted
pic_elements = [rec, testCircle]
# Add the elements onto the axes
for element in pic_elements:
ax.add_patch(element)
return ax

Categories