Last active
September 28, 2024 00:18
-
-
Save FedericoTartarini/2339ba7b50253fd9fb0ebbf3c0d7d739 to your computer and use it in GitHub Desktop.
gauge chart matplotlib
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import math | |
from dataclasses import dataclass | |
import matplotlib.pyplot as plt | |
import numpy as np | |
from PIL import Image | |
from scipy.interpolate import interp1d | |
def gauge_chart( | |
risk_value: float, | |
colors: list, | |
thresholds: list, | |
show_image: list = None, | |
text: list = None, | |
show_value: bool = True, | |
text_rotated: bool = True, | |
bottom: int = 1, | |
height: int = 2, | |
icon_size: int = 20, | |
): | |
m = interp1d([min(thresholds), max(thresholds)], [0.0, np.pi]) | |
x_axis_vals = [m(x) for x in thresholds] | |
width = [x - x_axis_vals[i - 1] for i, x in enumerate(x_axis_vals)][1:] | |
fig = plt.figure(figsize=(7, 4)) | |
ax = fig.add_subplot(projection="polar") | |
ax.set_thetamin(0) | |
ax.set_thetamax(180) | |
ax.bar( | |
x=x_axis_vals[:-1], | |
width=width, | |
height=height, | |
bottom=bottom, | |
linewidth=2, | |
edgecolor="white", | |
color=colors, | |
align="edge", | |
) | |
ax.set_axis_off() | |
ax.set_theta_zero_location("W") # theta=0 at the top | |
ax.set_theta_direction(-1) # theta increasing clockwise | |
color_annotation = "black" | |
for col, tre in zip(colors, thresholds): | |
if risk_value > tre: | |
color_annotation = col | |
if show_value: | |
ann = plt.annotate( | |
str(risk_value), | |
xytext=(0, 0), | |
xy=(m(risk_value), 1.5), | |
arrowprops=dict( | |
arrowstyle="wedge, tail_width=0.25", | |
# arrowstyle="-|>", | |
# arrowstyle="fancy", | |
color=color_annotation, | |
# shrinkA=0, | |
# linewidth=0.8, | |
ec="black", | |
), | |
bbox=dict( | |
boxstyle="circle", | |
pad=0.25, | |
# facecolor=color_annotation, | |
facecolor="none", | |
linewidth=0.8, | |
# ec="black", | |
ec="none", | |
), | |
fontsize=20, | |
color=color_annotation, | |
# color="black", | |
ha="center", | |
va="center", | |
) | |
ann.set_zorder(11) | |
plt.tight_layout(h_pad=0) | |
plt.subplots_adjust(top=1.3, bottom=-0.3, right=1, left=0, hspace=0, wspace=0) | |
if text: | |
for i, label in enumerate(text): | |
text_angle = 0 | |
if text_rotated: | |
text_angle = 180 - math.degrees(x_axis_vals[i] + width[i] / 2) | |
if text_angle > 90: | |
text_angle -= 180 | |
if show_image: | |
text_height = height - 0.5 | |
else: | |
text_height = height | |
txt = ax.text( | |
x_axis_vals[i] + width[i] / 2, | |
text_height, | |
label, | |
fontsize=12, | |
color="black", | |
ha="center", | |
va="center", | |
fontweight="bold", | |
rotation=text_angle, | |
) | |
if show_image: | |
txt.set_alpha(0) | |
else: | |
continue | |
# Get the bounding box of the text in display coordinates | |
bbox = txt.get_window_extent(renderer=fig.canvas.get_renderer()) | |
# Convert display coordinates to figure coordinates | |
bbox_fig = bbox.transformed(fig.transFigure.inverted()) | |
# Calculate the center point in figure coordinates | |
center_x = (bbox_fig.x0 + bbox_fig.x1) / 2 | |
center_y = (bbox_fig.y0 + bbox_fig.y1) / 2 | |
# Print the center point in figure coordinates | |
print(f"Center point in figure coordinates: ({center_x}, {center_y})") | |
# Convert figure coordinates to pixel coordinates | |
pixel_x = fig.bbox.x0 + center_x * fig.bbox.width | |
pixel_y = fig.bbox.y0 + center_y * fig.bbox.height | |
print(f"Figure coordinates: ({fig.bbox.x0}, {fig.bbox.y0})") | |
print(f"Figure width: {fig.bbox.width}") | |
print(f"Figure height: {fig.bbox.height}") | |
print(f"Center point in pixel coordinates: ({pixel_x}, {pixel_y})") | |
print("############################################") | |
image = Image.open(show_image[i]) | |
# Place the image at the calculated coordinates | |
fig.figimage( | |
image.resize([icon_size, icon_size]), | |
pixel_x - icon_size / 2, | |
pixel_y - icon_size / 2, | |
zorder=1, | |
) | |
txt = ax.text( | |
x_axis_vals[i] + width[i] / 2, | |
text_height+0.75, | |
label, | |
fontsize=12, | |
color="black", | |
ha="center", | |
va="center", | |
fontweight="bold", | |
rotation=text_angle, | |
) | |
plt.show() | |
if __name__ == "__main__": | |
@dataclass | |
class RiskValues: | |
risk_name: str | |
color: str | |
threshold: [float, float] | |
icon: str | |
model = [ | |
RiskValues("Risk 1", "tab:green", [0, 25], "icons/play-button.png"), | |
RiskValues("Risk 2", "tab:orange", [25, 100], "icons/water-bottle.png"), | |
RiskValues("Risk 3", "tab:red", [100, 150], "icons/pause.png"), | |
RiskValues("Risk 4", "tab:purple", [150, 200], "icons/no-stopping.png"), | |
] | |
colors = [risk.color for risk in model] | |
thresholds = [] | |
for risk in model: | |
thresholds.append(risk.threshold[0]) | |
thresholds.append(risk.threshold[1]) | |
thresholds = sorted(list(set(thresholds))) | |
icons = [risk.icon for risk in model] | |
text = [risk.risk_name for risk in model] | |
risk_value = 130 | |
gauge_chart( | |
risk_value=risk_value, | |
colors=colors, | |
thresholds=thresholds, | |
text=text, | |
# show_value=False, | |
text_rotated=True, | |
# show_image=icons, | |
icon_size=40, | |
# bottom=0 | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment