From b6cc4a4b247ecc7e377c70776cde8878d7e89a85 Mon Sep 17 00:00:00 2001 From: Guntis Smaukstelis Date: Sun, 23 Feb 2025 20:21:11 +0200 Subject: [PATCH] Show which slide show hours is fetched --- src/main/scala/data/DataService.scala | 4 +- src/main/scala/fetch/dmi/FetchService.scala | 2 +- web/src/harmonie/Harmonie.tsx | 4 +- web/src/harmonie/ReferenceTimes.tsx | 43 ++++++++++++--------- web/src/harmonie/SlideShow.tsx | 8 ++-- web/src/harmonie/harmonie.module.css | 10 +++++ web/src/helpers/progressivePromises.ts | 19 +++++++++ 7 files changed, 65 insertions(+), 25 deletions(-) create mode 100644 web/src/helpers/progressivePromises.ts diff --git a/src/main/scala/data/DataService.scala b/src/main/scala/data/DataService.scala index 332b68b..fb3f6f1 100644 --- a/src/main/scala/data/DataService.scala +++ b/src/main/scala/data/DataService.scala @@ -27,12 +27,14 @@ object DataService { class DataService(log: Logger[IO]) { val BASE_FOLDER = "data" val GRIB_FOLDER = s"$BASE_FOLDER/grib" + val TMP_FOLDER = s"$BASE_FOLDER/tmp" def init: IO[Unit] = { for { _ <- fs2.io.file.Files[IO].createDirectories(fs2.io.file.Path(BASE_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 () } diff --git a/src/main/scala/fetch/dmi/FetchService.scala b/src/main/scala/fetch/dmi/FetchService.scala index e3e2c7f..38d5327 100644 --- a/src/main/scala/fetch/dmi/FetchService.scala +++ b/src/main/scala/fetch/dmi/FetchService.scala @@ -78,7 +78,7 @@ class FetchService(dataService: DataService, log: Logger[IO]) { request = Request[IO](Method.GET, urlWithParams) // 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) .flatMap(_.body) .through(Files[IO].writeAll(tmpPath)) diff --git a/web/src/harmonie/Harmonie.tsx b/web/src/harmonie/Harmonie.tsx index 632f543..f444a9b 100644 --- a/web/src/harmonie/Harmonie.tsx +++ b/web/src/harmonie/Harmonie.tsx @@ -17,7 +17,7 @@ export const Harmonie: Component<{}> = () => { const [getIsInterpolated, setIsInterpolated] = createSignal(true) const [getGribList, setGribList] = createSignal([]) const [getCanvas, setCanvas] = createSignal() - const [getImgList, setImgList] = createSignal<[string, ImageBitmap][]>([]) + const [getImgList, setImgList] = createSignal<[string, ImageBitmap | undefined][]>([]) const cachedMessagesSignal = createSignal([]) const cachedBuffersSignal = createSignal([]) @@ -70,7 +70,7 @@ export const Harmonie: Component<{}> = () => { getGribList={getGribList} getCanvas={getCanvas} options={{ getIsCrop: getIsCrop, getIsContour: getIsContour, getIsInterpolated: getIsInterpolated }} - setImgList={setImgList} + imgListSignal={[getImgList, setImgList]} onClick={getAllGribStructure} />
    diff --git a/web/src/harmonie/ReferenceTimes.tsx b/web/src/harmonie/ReferenceTimes.tsx index f3ee03b..5c167cf 100644 --- a/web/src/harmonie/ReferenceTimes.tsx +++ b/web/src/harmonie/ReferenceTimes.tsx @@ -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 { fetchGribBinaries } from './fetchGrib' @@ -6,6 +6,7 @@ import { drawGrib } from './draw/drawGrib' import { CROP_BOUNDS } from './DrawView' import styles from './harmonie.module.css' +import { handleProgressivePromises } from '../helpers/progressivePromises' export const ReferenceTimes: Component<{ setIsLoading: Setter, @@ -13,7 +14,7 @@ export const ReferenceTimes: Component<{ getGribList: Accessor, getCanvas: Accessor, options: DrawOptions, - setImgList: Setter<[string, ImageBitmap][]>, + imgListSignal: Signal<[string, ImageBitmap | undefined][]>, onClick: () => void, }> = ({ setIsLoading, @@ -21,7 +22,7 @@ export const ReferenceTimes: Component<{ getGribList, getCanvas, options, - setImgList, + imgListSignal: [getImgList, setImgList], onClick, }) => { const [getActiveDate, setActiveDate] = createSignal('') @@ -40,16 +41,19 @@ export const ReferenceTimes: Component<{ setActiveDate(newValue) } - async function getImgList(dateStr: string) { + async function fetchDrawImgList(refDateStr: string) { setIsLoading(true) const canvas = getCanvas()! const cropBounds = options.getIsCrop() ? CROP_BOUNDS : undefined const contour = options.getIsContour() const isInterpolated = options.getIsInterpolated() 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) - // .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 [messages, buffers, bitmasks] = await fetchGribBinaries(grib, getGribList()) drawGrib(canvas, messages, buffers, bitmasks, cropBounds, contour, isInterpolated) @@ -59,17 +63,20 @@ export const ReferenceTimes: Component<{ return [grib.time.forecastTime, img] }) - Promise.all(promiseList) - .then(imgList => { - imgList.sort((a, b) => a[0] > b[0] ? 1 : -1) - setImgList(imgList) - }) - .finally(() => { - const ctx = canvas.getContext('2d')! - ctx.clearRect(0, 0, canvas.width, canvas.height) - setIsLoading(false) - }) - + handleProgressivePromises( + promiseList, + ([forecastDate, img]) => { + const udpdatedImgList = [...getImgList()] + const idx = udpdatedImgList.findIndex(([d]) => forecastDate === d) + if (idx >= 0) udpdatedImgList[idx][1] = img + setImgList(udpdatedImgList) + }, + ).finally(() => { + console.log("FINALLLY") + const ctx = canvas.getContext('2d')! + ctx.clearRect(0, 0, canvas.width, canvas.height) + setIsLoading(false) + }) } return
      @@ -81,7 +88,7 @@ export const ReferenceTimes: Component<{
      getImgList(dateStr)} + onClick={() => fetchDrawImgList(dateStr)} > { getGribList().some(g => g.time.referenceTime === dateStr) diff --git a/web/src/harmonie/SlideShow.tsx b/web/src/harmonie/SlideShow.tsx index 46b569d..0283b83 100644 --- a/web/src/harmonie/SlideShow.tsx +++ b/web/src/harmonie/SlideShow.tsx @@ -4,21 +4,23 @@ import styles from './harmonie.module.css' export const SlideShow: Component<{ getCanvas: Accessor, - getImgList: Accessor<[string, ImageBitmap][]>, + getImgList: Accessor<[string, ImageBitmap | undefined][]>, }> = ({ getCanvas, getImgList }) => { - function draw(img: ImageBitmap) { + function draw(img: ImageBitmap | undefined) { const canvas = getCanvas()! const ctx = canvas.getContext('2d')! ctx.clearRect(0, 0, canvas.width, canvas.height) + if (!img) return; + ctx.drawImage(img, 0, 0) } return
        { getImgList().map(([forecastDate, img]) => -
      • draw(img)}> +
      • draw(img)}> { format(forecastDate) }
      • )}
      diff --git a/web/src/harmonie/harmonie.module.css b/web/src/harmonie/harmonie.module.css index c00b251..bb2c8e1 100644 --- a/web/src/harmonie/harmonie.module.css +++ b/web/src/harmonie/harmonie.module.css @@ -70,7 +70,17 @@ padding: 10px 3px; } +.slideShow { + padding: 0; +} + .slideShow li { display: inline; + font-size: 14px; padding: 3px 3px; + border-radius: 5px; +} + +.withImg { + background-color: rgb(187, 247, 187); } \ No newline at end of file diff --git a/web/src/helpers/progressivePromises.ts b/web/src/helpers/progressivePromises.ts new file mode 100644 index 0000000..3aee7cc --- /dev/null +++ b/web/src/helpers/progressivePromises.ts @@ -0,0 +1,19 @@ +export async function handleProgressivePromises( + promises: Promise[], + 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 + ) +}