Initial shitty commit of skew-T chart rendering

This commit is contained in:
XANTRONIX Industrial 2025-02-23 15:26:52 -05:00
parent 2360baeebf
commit b96bc12660

194
lib/xmet/skew_t.py Normal file
View file

@ -0,0 +1,194 @@
#! /usr/bin/env python3
import math
import cairo
IMAGE_WIDTH = 1366
IMAGE_HEIGHT = 768
GRAPH_HEIGHT = IMAGE_HEIGHT - 64
GRAPH_WIDTH = (GRAPH_HEIGHT / 10.0) * 18
GRAPH_OFFSET_X = (IMAGE_WIDTH - GRAPH_WIDTH) / 2
GRAPH_OFFSET_Y = 32
PRESSURE_MAX = 1050
PRESSURE_MIN = 100
PRESSURE_STEP = 50
PRESSURE_LOG_MAX = math.log(PRESSURE_MAX)
PRESSURE_LOG_MIN = math.log(PRESSURE_MIN)
PRESSURE_LOG_RANGE = PRESSURE_LOG_MAX - PRESSURE_LOG_MIN
TEMP_CENTER = 0 # degrees C
TEMP_STEP = 5
TEMP_STEP_WIDTH = GRAPH_HEIGHT / 10.0
ADIABATIC_LAPSE_RATE_DRY = 9.8 / 1000 # degrees C per 1000m
ADIABATIC_LAPSE_RATE_MOIST = 4.0 / 1000
def clamp(value, lowest, highest):
if value < lowest:
return lowest
elif value > highest:
return highest
return value
def pressure_y(pressure: float) -> float:
if pressure < PRESSURE_MIN:
pressure = PRESSURE_MIN
elif pressure > PRESSURE_MAX:
pressure = PRESSURE_MAX
log_p = math.log(pressure)
factor = (PRESSURE_LOG_MAX - log_p) / PRESSURE_LOG_RANGE
return factor * GRAPH_HEIGHT
def graph_to_screen(x, y) -> tuple:
return (
GRAPH_OFFSET_X + x,
IMAGE_HEIGHT - GRAPH_OFFSET_Y - y
)
def draw_pressure_lines(cr: cairo.Context):
for pressure in range(PRESSURE_MIN, PRESSURE_MAX+1, PRESSURE_STEP):
(x, y) = graph_to_screen(0, pressure_y(pressure))
cr.set_source_rgba(0.0, 0.0, 0.0, 0.5)
cr.move_to(x, y)
cr.rel_line_to(GRAPH_WIDTH, 0)
cr.stroke()
def draw_graph_line(cr: cairo.Context, x1, y1, x2, y2):
(screen_x1, screen_y1) = graph_to_screen(x1, y1)
(screen_x2, screen_y2) = graph_to_screen(x2, y2)
cr.move_to(screen_x1, screen_y1)
cr.line_to(screen_x2, screen_y2)
cr.stroke()
ISOTHERM_LINES = (
(0, 0, 9, 9), (1, 0, 9, 8), (2, 0, 9, 7), (3, 0, 9, 6),
(4, 0, 9, 5), (5, 0, 9, 4), (6, 0, 9, 3), (7, 0, 9, 2),
(8, 0, 9, 1), (9, 0, 9, 0),
(-1, 0, 9, 10), (-2, 0, 8, 10), (-3, 0, 7, 10), (-4, 0, 6, 10),
(-5, 0, 5, 10), (-6, 0, 4, 10), (-7, 0, 3, 10), (-8, 0, 2, 10),
(-9, 0, 1, 10),
(-9, 1, 0, 10), (-9, 2, -1, 10), (-9, 3, -2, 10),
(-9, 4, -3, 10), (-9, 5, -4, 10), (-9, 6, -5, 10),
(-9, 7, -6, 10), (-9, 8, -7, 10), (-9, 9, -8, 10),
)
def isotherm_to_graph(x, y) -> tuple:
return (
GRAPH_WIDTH / 2 + x,
y
)
def draw_isotherms(cr: cairo.Context):
cr.set_source_rgba(0.1, 0.5, 0.1, 0.8)
for line in ISOTHERM_LINES:
x1 = line[0] * TEMP_STEP_WIDTH
y1 = line[1] * TEMP_STEP_WIDTH
x2 = line[2] * TEMP_STEP_WIDTH
y2 = line[3] * TEMP_STEP_WIDTH
graph_1 = isotherm_to_graph(x1, y1)
graph_2 = isotherm_to_graph(x2, y2)
draw_graph_line(cr, graph_1[0], graph_1[1], graph_2[0], graph_2[1])
def sample_to_graph(temp: float, pressure: float):
x = (temp / TEMP_STEP) * TEMP_STEP_WIDTH
y = pressure_y(pressure)
return (
GRAPH_WIDTH / 2 + x + y,
y
)
SAMPLES = (
( 101200, -6),
( 101200, -6),
( 100000, -12),
( 99300, -22),
( 98700, -29),
( 97500, -39),
( 95700, -50),
( 95100, -47),
( 93900, -43),
( 92000, -34),
( 90400, -34),
( 90000, -34),
( 89200, -34),
( 85000, -34),
( 83700, -34),
( 82100, -34),
( 80000, -42),
( 78600, -46),
( 77600, -50),
( 70000, -77),
( 69100, -80),
( 60700, -128),
( 60000, -133),
( 53100, -186),
( 50000, -215),
( 46300, -255),
( 40200, -337),
( 40000, -340),
( 35000, -412),
( 34900, -413),
( 30100, -496),
( 30000, -496),
( 25700, -565),
( 25000, -577),
( 24400, -585),
( 22100, -510),
( 21900, -510),
( 20000, -497),
( 15000, -466),
( 10000, -438),
( 7000, -414),
( 5000, -393),
( 3000, -368),
( 2000, -344),
( 1500, -337),
( 1000, -305),
( 500, -210),
( 400, -198)
)
with cairo.SVGSurface('test.svg', IMAGE_WIDTH, IMAGE_HEIGHT) as surface:
cr = cairo.Context(surface)
cr.rectangle(GRAPH_OFFSET_X, GRAPH_OFFSET_Y, GRAPH_WIDTH, GRAPH_HEIGHT)
cr.stroke()
draw_pressure_lines(cr)
draw_isotherms(cr)
cr.set_source_rgb(0, 0, 1)
first = True
for sample in SAMPLES:
pressure = sample[0] / 100.0
temp = sample[1] * 0.1
gx, gy = sample_to_graph(temp, pressure)
x, y = graph_to_screen(gx, gy)
print(f"x {x} y {y} pressure {pressure} temp {temp}")
if first:
cr.move_to(x, y)
first = False
else:
cr.line_to(x, y)
cr.stroke()