Show which slide show hours is fetched

This commit is contained in:
Guntis Smaukstelis
2025-02-23 20:21:11 +02:00
parent 6c9f544342
commit b6cc4a4b24
7 changed files with 65 additions and 25 deletions
+3 -1
View File
@@ -27,12 +27,14 @@ object DataService {
class DataService(log: Logger[IO]) { class DataService(log: Logger[IO]) {
val BASE_FOLDER = "data" val BASE_FOLDER = "data"
val GRIB_FOLDER = s"$BASE_FOLDER/grib" val GRIB_FOLDER = s"$BASE_FOLDER/grib"
val TMP_FOLDER = s"$BASE_FOLDER/tmp"
def init: IO[Unit] = { def init: IO[Unit] = {
for { for {
_ <- fs2.io.file.Files[IO].createDirectories(fs2.io.file.Path(BASE_FOLDER)) _ <- fs2.io.file.Files[IO].createDirectories(fs2.io.file.Path(BASE_FOLDER))
_ <- fs2.io.file.Files[IO].createDirectories(fs2.io.file.Path(GRIB_FOLDER)) _ <- fs2.io.file.Files[IO].createDirectories(fs2.io.file.Path(GRIB_FOLDER))
_ <- log.info(s"Created directories: $BASE_FOLDER and $GRIB_FOLDER") _ <- fs2.io.file.Files[IO].createDirectories(fs2.io.file.Path(TMP_FOLDER))
_ <- log.info(s"Created directories: $BASE_FOLDER, $GRIB_FOLDER, ${TMP_FOLDER}")
} yield () } yield ()
} }
+1 -1
View File
@@ -78,7 +78,7 @@ class FetchService(dataService: DataService, log: Logger[IO]) {
request = Request[IO](Method.GET, urlWithParams) request = Request[IO](Method.GET, urlWithParams)
// TODO proly better to call dataService method than property // TODO proly better to call dataService method than property
tmpPath = Path(s"${dataService.GRIB_FOLDER}/tmp.grib") tmpPath = Path(s"${dataService.TMP_FOLDER}/tmp.grib")
_ <- client.stream(request) _ <- client.stream(request)
.flatMap(_.body) .flatMap(_.body)
.through(Files[IO].writeAll(tmpPath)) .through(Files[IO].writeAll(tmpPath))
+2 -2
View File
@@ -17,7 +17,7 @@ export const Harmonie: Component<{}> = () => {
const [getIsInterpolated, setIsInterpolated] = createSignal(true) const [getIsInterpolated, setIsInterpolated] = createSignal(true)
const [getGribList, setGribList] = createSignal<GribMessage[]>([]) const [getGribList, setGribList] = createSignal<GribMessage[]>([])
const [getCanvas, setCanvas] = createSignal<HTMLCanvasElement>() const [getCanvas, setCanvas] = createSignal<HTMLCanvasElement>()
const [getImgList, setImgList] = createSignal<[string, ImageBitmap][]>([]) const [getImgList, setImgList] = createSignal<[string, ImageBitmap | undefined][]>([])
const cachedMessagesSignal = createSignal<GribMessage[]>([]) const cachedMessagesSignal = createSignal<GribMessage[]>([])
const cachedBuffersSignal = createSignal<Uint8Array[]>([]) const cachedBuffersSignal = createSignal<Uint8Array[]>([])
@@ -70,7 +70,7 @@ export const Harmonie: Component<{}> = () => {
getGribList={getGribList} getGribList={getGribList}
getCanvas={getCanvas} getCanvas={getCanvas}
options={{ getIsCrop: getIsCrop, getIsContour: getIsContour, getIsInterpolated: getIsInterpolated }} options={{ getIsCrop: getIsCrop, getIsContour: getIsContour, getIsInterpolated: getIsInterpolated }}
setImgList={setImgList} imgListSignal={[getImgList, setImgList]}
onClick={getAllGribStructure} onClick={getAllGribStructure}
/> />
<ul class={styles.fileList}> <ul class={styles.fileList}>
+25 -18
View File
@@ -1,4 +1,4 @@
import { Accessor, Component, createSignal, resetErrorBoundaries, Setter } from 'solid-js' import { Accessor, Component, createSignal, resetErrorBoundaries, Setter, Signal } from 'solid-js'
import { DrawOptions, GribMessage } from './interfaces' import { DrawOptions, GribMessage } from './interfaces'
import { fetchGribBinaries } from './fetchGrib' import { fetchGribBinaries } from './fetchGrib'
@@ -6,6 +6,7 @@ import { drawGrib } from './draw/drawGrib'
import { CROP_BOUNDS } from './DrawView' import { CROP_BOUNDS } from './DrawView'
import styles from './harmonie.module.css' import styles from './harmonie.module.css'
import { handleProgressivePromises } from '../helpers/progressivePromises'
export const ReferenceTimes: Component<{ export const ReferenceTimes: Component<{
setIsLoading: Setter<boolean>, setIsLoading: Setter<boolean>,
@@ -13,7 +14,7 @@ export const ReferenceTimes: Component<{
getGribList: Accessor<GribMessage[]>, getGribList: Accessor<GribMessage[]>,
getCanvas: Accessor<HTMLCanvasElement | undefined>, getCanvas: Accessor<HTMLCanvasElement | undefined>,
options: DrawOptions, options: DrawOptions,
setImgList: Setter<[string, ImageBitmap][]>, imgListSignal: Signal<[string, ImageBitmap | undefined][]>,
onClick: () => void, onClick: () => void,
}> = ({ }> = ({
setIsLoading, setIsLoading,
@@ -21,7 +22,7 @@ export const ReferenceTimes: Component<{
getGribList, getGribList,
getCanvas, getCanvas,
options, options,
setImgList, imgListSignal: [getImgList, setImgList],
onClick, onClick,
}) => { }) => {
const [getActiveDate, setActiveDate] = createSignal('') const [getActiveDate, setActiveDate] = createSignal('')
@@ -40,16 +41,19 @@ export const ReferenceTimes: Component<{
setActiveDate(newValue) setActiveDate(newValue)
} }
async function getImgList(dateStr: string) { async function fetchDrawImgList(refDateStr: string) {
setIsLoading(true) setIsLoading(true)
const canvas = getCanvas()! const canvas = getCanvas()!
const cropBounds = options.getIsCrop() ? CROP_BOUNDS : undefined const cropBounds = options.getIsCrop() ? CROP_BOUNDS : undefined
const contour = options.getIsContour() const contour = options.getIsContour()
const isInterpolated = options.getIsInterpolated() const isInterpolated = options.getIsInterpolated()
const forecastList = getGribList() const forecastList = getGribList()
.filter(g => g.time.referenceTime === dateStr) .filter(g => g.time.referenceTime === refDateStr)
.filter(g => g.meteo.discipline === 0 && g.meteo.category === 1 && g.meteo.product === 192) .filter(g => g.meteo.discipline === 0 && g.meteo.category === 1 && g.meteo.product === 192)
// .slice(0, 5) // TODO remove this const emptyImgList: [string, undefined][] = forecastList
.map((grib): [string, undefined] => [grib.time.forecastTime, undefined])
.sort((a, b) => a[0] > b[0] ? 1 : -1)
setImgList(emptyImgList)
const promiseList = forecastList.map(async (grib): Promise<[string, ImageBitmap]> => { const promiseList = forecastList.map(async (grib): Promise<[string, ImageBitmap]> => {
const [messages, buffers, bitmasks] = await fetchGribBinaries(grib, getGribList()) const [messages, buffers, bitmasks] = await fetchGribBinaries(grib, getGribList())
drawGrib(canvas, messages, buffers, bitmasks, cropBounds, contour, isInterpolated) drawGrib(canvas, messages, buffers, bitmasks, cropBounds, contour, isInterpolated)
@@ -59,17 +63,20 @@ export const ReferenceTimes: Component<{
return [grib.time.forecastTime, img] return [grib.time.forecastTime, img]
}) })
Promise.all(promiseList) handleProgressivePromises(
.then(imgList => { promiseList,
imgList.sort((a, b) => a[0] > b[0] ? 1 : -1) ([forecastDate, img]) => {
setImgList(imgList) const udpdatedImgList = [...getImgList()]
}) const idx = udpdatedImgList.findIndex(([d]) => forecastDate === d)
.finally(() => { if (idx >= 0) udpdatedImgList[idx][1] = img
const ctx = canvas.getContext('2d')! setImgList(udpdatedImgList)
ctx.clearRect(0, 0, canvas.width, canvas.height) },
setIsLoading(false) ).finally(() => {
}) console.log("FINALLLY")
const ctx = canvas.getContext('2d')!
ctx.clearRect(0, 0, canvas.width, canvas.height)
setIsLoading(false)
})
} }
return <ul class={styles.dateList}> return <ul class={styles.dateList}>
@@ -81,7 +88,7 @@ export const ReferenceTimes: Component<{
<div <div
class={styles.controls} class={styles.controls}
style={{ display: getActiveDate() === dateStr ? 'block' : 'none'}} style={{ display: getActiveDate() === dateStr ? 'block' : 'none'}}
onClick={() => getImgList(dateStr)} onClick={() => fetchDrawImgList(dateStr)}
> >
{ {
getGribList().some(g => g.time.referenceTime === dateStr) getGribList().some(g => g.time.referenceTime === dateStr)
+5 -3
View File
@@ -4,21 +4,23 @@ import styles from './harmonie.module.css'
export const SlideShow: Component<{ export const SlideShow: Component<{
getCanvas: Accessor<HTMLCanvasElement | undefined>, getCanvas: Accessor<HTMLCanvasElement | undefined>,
getImgList: Accessor<[string, ImageBitmap][]>, getImgList: Accessor<[string, ImageBitmap | undefined][]>,
}> = ({ }> = ({
getCanvas, getCanvas,
getImgList getImgList
}) => { }) => {
function draw(img: ImageBitmap) { function draw(img: ImageBitmap | undefined) {
const canvas = getCanvas()! const canvas = getCanvas()!
const ctx = canvas.getContext('2d')! const ctx = canvas.getContext('2d')!
ctx.clearRect(0, 0, canvas.width, canvas.height) ctx.clearRect(0, 0, canvas.width, canvas.height)
if (!img) return;
ctx.drawImage(img, 0, 0) ctx.drawImage(img, 0, 0)
} }
return <ul class={styles.slideShow}> return <ul class={styles.slideShow}>
{ getImgList().map(([forecastDate, img]) => { getImgList().map(([forecastDate, img]) =>
<li onClick={() => draw(img)}> <li class={img ? styles.withImg : ''} onClick={() => draw(img)}>
{ format(forecastDate) } { format(forecastDate) }
</li>)} </li>)}
</ul> </ul>
+10
View File
@@ -70,7 +70,17 @@
padding: 10px 3px; padding: 10px 3px;
} }
.slideShow {
padding: 0;
}
.slideShow li { .slideShow li {
display: inline; display: inline;
font-size: 14px;
padding: 3px 3px; padding: 3px 3px;
border-radius: 5px;
}
.withImg {
background-color: rgb(187, 247, 187);
} }
+19
View File
@@ -0,0 +1,19 @@
export async function handleProgressivePromises<T>(
promises: Promise<T>[],
onProgress: (result: T) => void,
): Promise<(T | undefined)[]> {
const allPromises = promises.map(async promise => {
try {
const result = await promise;
onProgress(result)
return result
} catch (error) {
return undefined
}
})
const results = await Promise.allSettled(allPromises)
return results.map(result =>
result.status === 'fulfilled' ? result.value : undefined
)
}