From cb2a75d5fd4d424e28ef828f5271964afa98769d Mon Sep 17 00:00:00 2001 From: Guntis Smaukstelis Date: Sat, 15 Mar 2025 21:34:44 +0200 Subject: [PATCH] Add morning and evening broadcast forecasts --- web/src/assets/bg-2-dienas.webp | Bin 0 -> 9710 bytes web/src/components/table/Table.tsx | 54 ++++++++-------- web/src/pages/lvgmc-forecast/CsvMapData.tsx | 59 +++++++++--------- .../pages/lvgmc-forecast/LvgmcForecast.tsx | 48 ++++++++++---- 4 files changed, 89 insertions(+), 72 deletions(-) create mode 100644 web/src/assets/bg-2-dienas.webp diff --git a/web/src/assets/bg-2-dienas.webp b/web/src/assets/bg-2-dienas.webp new file mode 100644 index 0000000000000000000000000000000000000000..34fc23f437af5e613923d6e22ec55dcca8f304f0 GIT binary patch literal 9710 zcmeHMc|26@+dpGpvxe*;3Bwr6Fxj`r8nQ%b%)(@uF~%;GM9H3_2qDUnJl14+vXm?( zTOnJvibS$xnfHvA=l6R*&+mQzen0PXW{&ec_kCU8`?|jOeV>oHj*Y3Y@$4Z0a4^!h zwzpPyWB~v`6FfQS04^H9)WEN(pau2; z+<+jUjzaqp^{lPUsn&mgtPKJXYEK`O%Jug)|FM-FgY`oL07M+L&_xqTWDuVPv05OR zNX6+O=0$s;Fd!}gu{;S35X5h&?QTEt0u{UO;k~4INp>~{U~ar%ySV$mV7Gt4Xb+M% zXrlt!NMXEvKz9~eW$VN~qx;9nXX4ggjP0f5~J0C-*j0E_bv-M~I-Kae*B0Cu2O z=G_30aSH&X-9c`b|ByEm=d)wd1(_s{BZ<2a)iMBZ)A zZ!Fma8Zt3+mnhWfAcKlEuh=~fBY*jvyEu^*=U8!Xae{vLz0Ti!g`z^!9`P3Dx)ix} zaIk!FHT*+NjPTT^rAhMiv+%(!s0)fI zl<%*5t2U$=J3^AY8GKUu8#rso*M<} zv@s9Y6-llSbmotbOgQ)uAlGh73B1-}d3ith(8%p(?7WkOIA=la(HKNl~H?~7ppo=t2GY4rF+oK_tbg)Z?&b!oyla{=d7?l zstVChZ(r@JkUNzSeRKK^mP&R`qa)rTx#Mm36QouFMm80Ao4a~xU!t>iX2t%g(>{sL z0{pV))@05fq|FN*%VQZ5s>ztU4MkZu^MFKREZ{;RoG$VO$I`1cK^~AwAMVm+(_VD$@`Pl6jZ4B!# z1Jg{LXpCs+a|e#qKBbi@Wbfmgo^Ng5J^&RiK3OZiD0wkfi@kjq!oIkfbRkTpns_~T zN3sU*IsD-Bsm#|y5mNX=4^pS}cy3MQc$T-xJI>|#Vfc%Z2TvmE+krU38S~kwde-~) z#R+!~onT^Nr#z^ldBA7+q&TMi<4j6OZFX7D>2d$VV-TKIA6M%+Zk(QD`o!sj(XKZu zOxMGu^3WXK6pos{39Ma7?(M(@ICX-G*+yO&e{>J^PUvlMbil_FG zRgW1id^KW*w@JDNIhdH6@hu{%XZnGKMqhLHK)qGSChugvp`+BrF&o|3O=g4KV~MpP z1+BSH|3DSYFf`~2demE}sTIGiMbsTxLQFC&)I}FK>Au%W-s#x~Q@AYC∨*I)%4+ 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 TQ{b, }> = ({ - csvUrl, + getCsvLines, }) => { let canvas: HTMLCanvasElement | undefined const rigaBg = new Image() @@ -25,29 +23,29 @@ export const Table: Component<{ const arrowImg = new Image() arrowImg.src = arrowUrl - fetchText(`${apiHost}/api/show/lvgmc-forecast/${csvUrl}`) - .then(csv => { - const csvLines = csv.split('\n') - const dateStr = csvLines[2].split(';')[0].split('.').reverse().join('.') - const weekDay = new Date(dateStr).getDay() - const weekDayLV = weekDaysLV.get(weekDay)?.toUpperCase() ?? '' - const dayOrNight = csvLines[2].split(';')[1] - const rigaOffset = dayOrNight === 'DIENA' ? 24 : 51 - const data: TableWeather[] = csvLines - .slice(rigaOffset, rigaOffset+4) - .map((line): TableWeather => { - const parts = line.split(';') - const hour = parts[0].slice(6, 11).replace(':', '.') - const temperature = parts[1] - const windDirection = parts[3] - const windSpeed = parts[5] - const iconCode = Number(parts[7]) - const icon = codeToIcon(iconCode) + createEffect(() => { + const csvLines = getCsvLines() + if (csvLines.length <= 0) return; + const dateStr = csvLines[2].split(';')[0].split('.').reverse().join('.') + const weekDay = new Date(dateStr).getDay() + const weekDayLV = weekDaysLV.get(weekDay)?.toUpperCase() ?? '' + const dayOrNight = csvLines[2].split(';')[1] + const rigaOffset = dayOrNight === 'DIENA' ? 24 : 51 + const data: TableWeather[] = csvLines + .slice(rigaOffset, rigaOffset+4) + .map((line): TableWeather => { + const parts = line.split(';') + const hour = parts[0].slice(6, 11).replace(':', '.') + const temperature = parts[1] + const windDirection = parts[3] + const windSpeed = parts[5] + const iconCode = Number(parts[7]) + const icon = codeToIcon(iconCode) - return { hour, temperature, windDirection, windSpeed, icon } - }) - drawTable(canvas!, rigaBg, arrowImg, data, weekDayLV) - }) + return { hour, temperature, windDirection, windSpeed, icon } + }) + drawTable(canvas!, rigaBg, arrowImg, data, weekDayLV) + }) return <> @@ -104,11 +102,11 @@ function drawTable( } const weekDaysLV = new Map([ + [0, 'svētdiena'], [1, 'pirmdiena'], [2, 'otrdiena'], [3, 'trešdiena'], [4, 'ceturtdiena'], [5, 'piektdiena'], [6, 'sestdiena'], - [7, 'svētdiena'], ]) \ No newline at end of file diff --git a/web/src/pages/lvgmc-forecast/CsvMapData.tsx b/web/src/pages/lvgmc-forecast/CsvMapData.tsx index 8c1ef94..0609f50 100644 --- a/web/src/pages/lvgmc-forecast/CsvMapData.tsx +++ b/web/src/pages/lvgmc-forecast/CsvMapData.tsx @@ -1,4 +1,4 @@ -import { Component, createSignal, onMount } from 'solid-js' +import { Accessor, Component, createEffect, createSignal, onMount } from 'solid-js' import { apiHost } from '../../consts' import { fetchText } from '../../helpers/fetch' import { codeToIcon } from '../../components/weatherIcons/iconConsts' @@ -10,43 +10,40 @@ import arrowUrl from '../../assets/arrow.webp' import { resolutionProps, windProps, WindProps } from '../../components/map/mapConsts' export const CsvMapData: Component<{ - csvUrl: string, + getCityLines: Accessor, + getWindLine: Accessor, bgImgUrl: string, }> = ({ - csvUrl, + getCityLines, + getWindLine, bgImgUrl, }) => { const [getCityData, setCityData] = createSignal([]) const [getWindData, setWindData] = createSignal<[string, string, string]>(['', '', '']) - - fetchText(`${apiHost}/api/show/lvgmc-forecast/${csvUrl}`) - .then(csv => { - const csvLines = csv.split('\n') - const dayOrNight = csvLines[2].split(';')[1] - // console.log(csvLines) - const citiesOffset = 4 - const cityData: CityData[] = csvCityListInOrder - .map((city, i): CityData => { - const parts = csvLines[i + citiesOffset].split(';') - const temperature = Number(parts[1]) - const iconCode = Number(parts[2]) - const icon = codeToIcon(iconCode) - const coord = cityCoords[city] - if (!coord) throw new Error(`No coord found for city: ${city}`) - return [city, coord, temperature, icon] - }) - .filter(([city]) => citiesToShow.includes(city)) - - const windOffset = dayOrNight === 'DIENA' ? 30 : 26 - const windParts = csvLines[windOffset].split(';') - const windSpped = windParts[1] - const windGusts = windParts[3] - const windDirection = windParts[5] - setWindData([windDirection, windSpped, windGusts]) - setCityData(cityData) - draw() - }) + createEffect(() => { + if (getCityLines().length <= 0) return; + const cityData: CityData[] = csvCityListInOrder + .map((city, i): CityData => { + const parts = getCityLines()[i].split(';') + const temperature = Number(parts[1]) + const iconCode = Number(parts[2]) + const icon = codeToIcon(iconCode) + const coord = cityCoords[city] + if (!coord) throw new Error(`No coord found for city: ${city}`) + return [city, coord, temperature, icon] + }) + .filter(([city]) => citiesToShow.includes(city)) + + const windParts = getWindLine().split(';') + const windSpped = windParts[1] + const windGusts = windParts[3] + const windDirection = windParts[5] + setWindData([windDirection, windSpped, windGusts]) + setCityData(cityData) + + draw() + }) const arrowImg = new Image() arrowImg.src = arrowUrl diff --git a/web/src/pages/lvgmc-forecast/LvgmcForecast.tsx b/web/src/pages/lvgmc-forecast/LvgmcForecast.tsx index e8c03c6..05f7057 100644 --- a/web/src/pages/lvgmc-forecast/LvgmcForecast.tsx +++ b/web/src/pages/lvgmc-forecast/LvgmcForecast.tsx @@ -1,9 +1,11 @@ -import { Component } from 'solid-js' +import { Component, createEffect, createSignal, Setter } from 'solid-js' import dayBgMap from '../../assets/map_3840x1440_wind.webp' import nightBgMap from '../../assets/map_night_3840x1440_wind.webp' import { CsvMapData } from './CsvMapData' import { Table } from '../../components/table/Table' +import { apiHost } from '../../consts' +import { fetchText } from '../../helpers/fetch' // Eiropa_LTV_pilsetas_nakama_dn.csv // Eiropa_LTV_pilsetas_tekosa_dn.csv @@ -12,24 +14,44 @@ import { Table } from '../../components/table/Table' // Latvija_faktiskais_laiks.csv export const LvgmcForecast: Component<{}> = () => { - return <> - -
+ const [getEveningLines, setEveningLines] = createSignal([]) + const [getMorningLines, setMorningLines] = createSignal([]) -

Night

+ const urlsAndSignals: [string, Setter][] = [ + ['Latvija_LTV_pilsetas_nakama_dnn.csv', setEveningLines], + ['Latvija_LTV_pilsetas_tekosa_dn.csv', setMorningLines], + ] + urlsAndSignals.forEach(([csvUrl, setLines]) => + fetchText(`${apiHost}/api/show/lvgmc-forecast/${csvUrl}`) + .then(csv => setLines(csv.split('\n'))) + .catch(console.warn) + ) + + createEffect(() => { + console.log(getMorningLines()) + }) + + + return <> +

Vakara ēters

getEveningLines().slice(4, 22)} + getWindLine={() => getEveningLines()[26]} bgImgUrl={nightBgMap} /> - -

Day

getEveningLines().slice(31, 49)} + getWindLine={() => getEveningLines()[57]} bgImgUrl={dayBgMap} /> +
getEveningLines()} /> + +

Rīta ēters

+ getMorningLines().slice(4, 22)} + getWindLine={() => getMorningLines()[23]} + bgImgUrl={dayBgMap} + /> +
getMorningLines()} /> }