Gauge Chart

Air Quality Meter

Environmental AQI reading with adaptive zones

Output
Air Quality Meter
Python
import matplotlib.pyplot as plt
from matplotlib.patches import Wedge, Circle, Polygon
import numpy as np

# Data
aqi = 52
max_aqi = 300
value = (aqi / max_aqi) * 100

# Dynamic color
if aqi <= 50:
    arc_color = '#27F5B0'
    status = 'Good'
elif aqi <= 100:
    arc_color = '#F5D327'
    status = 'Moderate'
elif aqi <= 150:
    arc_color = '#F5B027'
    status = 'Sensitive'
else:
    arc_color = '#F54927'
    status = 'Unhealthy'

# Colors - Light theme
bg_color = '#ffffff'
track_color = '#f3f4f6'
text_color = '#1f2937'
dim_color = '#9ca3af'

start_angle, end_angle = -30, 210
sweep = end_angle - start_angle

fig, ax = plt.subplots(figsize=(10, 8), facecolor=bg_color)
ax.set_facecolor(bg_color)
ax.set_xlim(-1.4, 1.4)
ax.set_ylim(-0.5, 1.4)
ax.set_aspect('equal')
ax.axis('off')

# AQI zones
zones = [(0, 50, '#dcfce7'), (50, 100, '#fef9c3'), (100, 150, '#fed7aa'), 
         (150, 200, '#fecaca'), (200, 300, '#f3e8ff')]
for z_start, z_end, z_color in zones:
    z_s_pct = z_start / max_aqi
    z_e_pct = z_end / max_aqi
    z_s_ang = end_angle - z_s_pct * sweep
    z_e_ang = end_angle - z_e_pct * sweep
    ax.add_patch(Wedge((0, 0), 1.0, z_e_ang, z_s_ang, width=0.15,
                       facecolor=z_color, edgecolor='none'))

# Value arc
value_pct = value / 100
value_angle = start_angle + (1 - value_pct) * sweep
for r_off, w_off, alpha in [(0.03, 0.04, 0.2), (0.015, 0.02, 0.45), (0, 0, 0.95)]:
    ax.add_patch(Wedge((0, 0), 1.0 + r_off, value_angle, end_angle,
                       width=0.15 + w_off, facecolor=arc_color, edgecolor='none', alpha=alpha))

# Ticks
tick_vals = [0, 50, 100, 150, 200, 300]
for val in tick_vals:
    pct = val / max_aqi
    angle = np.radians(end_angle - pct * sweep)
    ax.plot([0.87*np.cos(angle), 0.80*np.cos(angle)], 
            [0.87*np.sin(angle), 0.80*np.sin(angle)], color='#d1d5db', linewidth=2)
    ax.text(0.72*np.cos(angle), 0.72*np.sin(angle), f'{val}',
            fontsize=9, color=dim_color, ha='center', va='center')

# Needle
needle_angle = np.radians(end_angle - value_pct * sweep)
nx, ny = 0.62 * np.cos(needle_angle), 0.62 * np.sin(needle_angle)
perp = needle_angle + np.pi/2
bx1, by1 = 0.035 * np.cos(perp), 0.035 * np.sin(perp)

ax.add_patch(Polygon([(nx+0.01, ny-0.01), (bx1+0.01, by1-0.01), (-bx1+0.01, -by1-0.01)],
                     facecolor='#000000', edgecolor='none', alpha=0.1))
ax.add_patch(Polygon([(nx, ny), (bx1, by1), (-bx1, -by1)], facecolor=text_color, edgecolor='none'))

# Hub
ax.add_patch(Circle((0.01, -0.01), 0.09, facecolor='#000000', edgecolor='none', alpha=0.08))
for r, c, a in [(0.10, arc_color, 0.2), (0.08, '#ffffff', 1), (0.06, arc_color, 0.9)]:
    ax.add_patch(Circle((0, 0), r, facecolor=c, edgecolor='none', alpha=a))

# Text
ax.text(0, -0.22, f'{aqi}', fontsize=56, fontweight='bold', color=text_color, ha='center', va='center')
ax.text(0, -0.40, 'AQI', fontsize=14, color=dim_color, ha='center', va='center', fontweight='500')
ax.text(0, -0.56, status.upper(), fontsize=14, color=arc_color, ha='center', va='center', fontweight='bold')
ax.text(0, 1.22, 'Air Quality Index', fontsize=18, fontweight='bold', color=text_color, ha='center', va='center')

plt.tight_layout()
plt.show()
Library

Matplotlib

Category

Part-to-Whole

Did this help you?

Support PyLucid to keep it free & growing

Support