Rework graphs to widget
ci/woodpecker/push/woodpecker Pipeline was successful
Details
ci/woodpecker/push/woodpecker Pipeline was successful
Details
This commit is contained in:
parent
1c570ea2a0
commit
557df68c75
|
|
@ -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(),
|
||||||
const y = tempY
|
y: h.temperature as number,
|
||||||
const temperature = hour.temperature as number
|
})),
|
||||||
const ratio = (temperature - minTemp) / tempRange
|
xAxisFont: 3,
|
||||||
const height = Math.floor(50 * ratio)
|
yAxisFont: 3,
|
||||||
const t = now.plus({ hours: offsetInt }).hour
|
adjustMinValue: (min) => Math.min(0, min - 5),
|
||||||
|
showMinValue: true,
|
||||||
widgets.push({
|
showMaxValue: true,
|
||||||
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,51 +253,24 @@ 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]) => ({
|
||||||
const y = preY
|
x: now.plus({ hours: +offset }).hour.toString().padStart(2, '0'),
|
||||||
const pre = hour.precipitationAmount as number
|
yFormatted: (h.precipitationAmount as number).toFixed(1) + 'mm',
|
||||||
const ratio = (pre - minPre) / preRange
|
y: h.precipitationAmount as number,
|
||||||
const height = Math.floor(50 * ratio)
|
})),
|
||||||
const t = now.plus({ hours: offsetInt }).hour
|
xAxisFont: 3,
|
||||||
|
yAxisFont: 3,
|
||||||
widgets.push({
|
adjustMinValue: () => 0,
|
||||||
x: x + 7,
|
adjustMaxValue: (max) => Math.max(1, max),
|
||||||
y: y + 5 + 50 - height,
|
showMaxValue: true,
|
||||||
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)
|
||||||
}),
|
}),
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
},
|
||||||
|
)
|
||||||
Loading…
Reference in New Issue