Rework graphs to widget
ci/woodpecker/push/woodpecker Pipeline was successful Details

This commit is contained in:
Jan Zípek 2024-06-30 09:53:13 +02:00
parent 1c570ea2a0
commit 557df68c75
Signed by: kamen
GPG Key ID: A17882625B33AC31
2 changed files with 176 additions and 139 deletions

View File

@ -8,6 +8,7 @@ import { ALIGN } from '../../lib/dow/constants'
import { DowWidget } from '../../lib/dow/constants' import { DowWidget } from '../../lib/dow/constants'
import { rectangleWithTitle } from '../../lib/dow/widgets/rectangleWithTitle' import { rectangleWithTitle } from '../../lib/dow/widgets/rectangleWithTitle'
import { text } from '../../lib/dow/primitives/text' import { text } from '../../lib/dow/primitives/text'
import { barGraph } from '../../lib/dow/widgets/barGraph'
const weekdaysInCs = [ const weekdaysInCs = [
'Pondeli', 'Pondeli',
@ -167,56 +168,17 @@ export const dowRoutes = router((router, ctx) => {
const next12Hours = Object.entries(weather.hours).slice(1, 22) const next12Hours = Object.entries(weather.hours).slice(1, 22)
const minTemp = const tempY = 230
Math.min(...next12Hours.map(([, h]) => h.temperature as number)) - 5
const minTempIndex = Object.entries(weather.hours).reduce(
(acc, [i, h]) => {
if (typeof h.temperature !== 'number') {
return acc
}
if (!acc || h.temperature < acc.temp) {
return { temp: h.temperature, index: i }
}
return acc
},
{} as { temp: number; index: string } | undefined,
)?.index
const maxTemp = Math.max(
...next12Hours.map(([, h]) => h.temperature as number),
)
const maxTempIndex = Object.entries(weather.hours).reduce(
(acc, [i, h]) => {
if (typeof h.temperature !== 'number') {
return acc
}
if (!acc || h.temperature > acc.temp) {
return { temp: h.temperature, index: i }
}
return acc
},
{} as { temp: number; index: string } | undefined,
)?.index
const tempRange = maxTemp - minTemp
const tempY = 250
const tempX = 30 const tempX = 30
const tempW = 280 const tempW = 280
const tempH = 120 const tempH = 120
const tempGraphP = 10 const tempGraphP = 5
const tempGraphW = tempW - tempGraphP * 2 const tempGraphW = tempW - tempGraphP * 4
widgets.push( widgets.push(
...rectangleWithTitle({ ...rectangleWithTitle({
x: tempX, x: tempX,
y: tempY - 40, y: tempY - 20,
width: tempW, width: tempW,
height: tempH, height: tempH,
title: 'Teplota', title: 'Teplota',
@ -225,54 +187,26 @@ export const dowRoutes = router((router, ctx) => {
}), }),
) )
for (const [offset, hour] of next12Hours) { widgets.push(
const offsetInt = +offset ...barGraph({
x: tempX + tempGraphP * 2,
const x = Math.floor( y: tempY + tempGraphP,
tempX + width: tempGraphW,
tempGraphP + height: tempH - 20 - tempGraphP * 2,
(offsetInt - 1) * (tempGraphW / next12Hours.length), data: next12Hours.map(([offset, h]) => ({
x: now.plus({ hours: +offset }).hour.toString().padStart(2, '0'),
yFormatted: Math.floor(h.temperature as number).toString(),
y: h.temperature as number,
})),
xAxisFont: 3,
yAxisFont: 3,
adjustMinValue: (min) => Math.min(0, min - 5),
showMinValue: true,
showMaxValue: true,
}),
) )
const y = tempY /*
const temperature = hour.temperature as number
const ratio = (temperature - minTemp) / tempRange
const height = Math.floor(50 * ratio)
const t = now.plus({ hours: offsetInt }).hour
widgets.push({
x: x + 7,
y: y + 5 + 50 - height,
x2: 6,
y2: height,
t: TYPES.RECT_FILL,
})
if (offset === minTempIndex || offset === maxTempIndex) {
widgets.push({
x: x + 10,
y: y,
c: Math.floor(hour.temperature as number).toString(),
t: TYPES.TEXT,
va: ALIGN.END,
ha: ALIGN.CENTER,
f: 3,
})
}
if ((offsetInt - 1) % 2 === 0) {
widgets.push({
x: x + 10,
y: y + 60,
c: t.toString().padStart(2, '0'),
t: TYPES.TEXT,
va: ALIGN.START,
ha: ALIGN.CENTER,
f: 3,
})
}
}
const minPre = Math.min( const minPre = Math.min(
...next12Hours.map(([, h]) => h.precipitationAmount as number), ...next12Hours.map(([, h]) => h.precipitationAmount as number),
) )
@ -298,18 +232,19 @@ export const dowRoutes = router((router, ctx) => {
const displayedMaxPre = Math.max(maxPre, 1) const displayedMaxPre = Math.max(maxPre, 1)
const preRange = displayedMaxPre - minPre const preRange = displayedMaxPre - minPre
*/
const preY = 250 + 120 + 20 const preY = 250 + 120
const preX = 30 const preX = 30
const preW = 280 const preW = 280
const preH = 120 const preH = 120
const preGraphP = 10 const preGraphP = 5
const preGraphW = preW - preGraphP * 2 const preGraphW = preW - preGraphP * 4
widgets.push( widgets.push(
...rectangleWithTitle({ ...rectangleWithTitle({
x: preX, x: preX,
y: preY - 40, y: preY - 20,
width: preW, width: preW,
height: preH, height: preH,
title: 'Srazky', title: 'Srazky',
@ -318,52 +253,25 @@ export const dowRoutes = router((router, ctx) => {
}), }),
) )
for (const [offset, hour] of next12Hours) { widgets.push(
const offsetInt = +offset ...barGraph({
x: preX + preGraphP * 2,
const x = Math.floor( y: preY + preGraphP,
preX + preGraphP + (offsetInt - 1) * (preGraphW / next12Hours.length), width: preGraphW,
height: preH - 20 - preGraphP * 2,
data: next12Hours.map(([offset, h]) => ({
x: now.plus({ hours: +offset }).hour.toString().padStart(2, '0'),
yFormatted: (h.precipitationAmount as number).toFixed(1) + 'mm',
y: h.precipitationAmount as number,
})),
xAxisFont: 3,
yAxisFont: 3,
adjustMinValue: () => 0,
adjustMaxValue: (max) => Math.max(1, max),
showMaxValue: true,
}),
) )
const y = preY
const pre = hour.precipitationAmount as number
const ratio = (pre - minPre) / preRange
const height = Math.floor(50 * ratio)
const t = now.plus({ hours: offsetInt }).hour
widgets.push({
x: x + 7,
y: y + 5 + 50 - height,
x2: 6,
y2: height,
t: TYPES.RECT_FILL,
})
if (offset === maxPreIndex && maxPre > 0.01) {
widgets.push({
x: x + 10,
y: y,
c: (hour.precipitationAmount as number).toFixed(1) + 'mm',
t: TYPES.TEXT,
va: ALIGN.END,
ha: ALIGN.CENTER,
f: 3,
})
}
if ((offsetInt - 1) % 2 === 0) {
widgets.push({
x: x + 10,
y: y + 60,
c: t.toString().padStart(2, '0'),
t: TYPES.TEXT,
va: ALIGN.START,
ha: ALIGN.CENTER,
f: 3,
})
}
}
res.json(widgets) res.json(widgets)
}), }),
) )

View File

@ -0,0 +1,129 @@
import { ALIGN, DowWidget } from '../constants'
import { defineWidget } from '../defineWidget'
import { rectangle } from '../primitives/rectangle'
import { text } from '../primitives/text'
type Params = {
data: {
x: string
y: number
yFormatted?: string
}[]
showMaxValue?: boolean
showMinValue?: boolean
adjustMaxValue?: (max: number) => number
adjustMinValue?: (min: number) => number
x: number
y: number
width: number
height: number
xAxisFont: number
yAxisFont: number
}
export const barGraph = defineWidget(
({
data,
showMaxValue,
showMinValue,
adjustMaxValue,
adjustMinValue,
x,
y,
width,
height,
xAxisFont,
yAxisFont,
}: Params) => {
if (data.length === 0) {
return []
}
const minValue = data.reduce(
(acc, { y }, index) =>
!acc || y < acc?.value ? { value: y, index } : acc,
undefined as { value: number; index: number } | undefined,
)
const maxValue = data.reduce(
(acc, { y }, index) =>
!acc || y > acc?.value ? { value: y, index } : acc,
undefined as { value: number; index: number } | undefined,
)
if (!minValue || !maxValue) {
return []
}
const adjustedMinValue = adjustMinValue
? adjustMinValue(minValue.value)
: minValue.value
const adjustedMaxValue = adjustMaxValue
? adjustMaxValue(maxValue.value)
: maxValue.value
const valueRange = adjustedMaxValue - adjustedMinValue
const widgets = [] as DowWidget[]
// DEBUG: widgets.push(rectangle({ x, y, width, height, filled: false }))
const xAxisHeight = 25
const yAxisHeight = 20
const barsHeight = height - xAxisHeight - yAxisHeight
const barSpaceWidth = width / data.length
const barsWidth = 6
for (const [index, dataPoint] of data.entries()) {
const barX = Math.floor(x + index * barSpaceWidth)
const barY = y + yAxisHeight
const value = dataPoint.y
const ratio = (value - adjustedMinValue) / valueRange
const barHeight = Math.floor(barsHeight * ratio)
widgets.push(
rectangle({
x: Math.floor(barX + barSpaceWidth / 2 - barsWidth / 2),
y: barY + 5 + barsHeight - barHeight,
width: barsWidth,
height: barHeight,
filled: true,
}),
)
if (
(index === minValue.index && showMinValue) ||
(index === maxValue.index && showMaxValue)
) {
widgets.push(
text({
x: Math.floor(barX + barSpaceWidth / 2),
y: barY,
text: dataPoint.yFormatted ?? dataPoint.y.toString(),
verticalAlign: ALIGN.END,
horizontalAlign: ALIGN.CENTER,
font: yAxisFont,
}),
)
}
if (index % 2 === 0) {
widgets.push(
text({
x: Math.floor(barX + barSpaceWidth / 2),
y: barY + barsHeight + 10,
text: dataPoint.x,
verticalAlign: ALIGN.START,
horizontalAlign: ALIGN.CENTER,
font: xAxisFont,
}),
)
}
}
return widgets
},
)