Basic slideshow functionality

This commit is contained in:
Guntis Smaukstelis
2025-02-21 23:07:49 +02:00
parent 832f723f39
commit 2893b5acff
7 changed files with 110 additions and 9 deletions
+3 -3
View File
@@ -4,23 +4,23 @@ import { LoadingSpinner } from '../components/LoadingSpinner'
import { DrawOptions, GribMessage } from './interfaces' import { DrawOptions, GribMessage } from './interfaces'
import { drawGrib } from './draw/drawGrib' import { drawGrib } from './draw/drawGrib'
const CROP_BOUNDS = { x: 1906-1-440, y: 895, width: 440, height: 380 } export const CROP_BOUNDS = { x: 1906-1-440, y: 895, width: 440, height: 380 }
export const DrawView: Component<{ export const DrawView: Component<{
isLoadingSignal: Signal<boolean>, isLoadingSignal: Signal<boolean>,
options: DrawOptions; options: DrawOptions;
canvasSignal: Signal<HTMLCanvasElement | undefined>,
cachedMessagesSignal: Signal<GribMessage[]>, cachedMessagesSignal: Signal<GribMessage[]>,
cachedBuffersSignal: Signal<Uint8Array[]>, cachedBuffersSignal: Signal<Uint8Array[]>,
cachedBitmasksSignal: Signal<Uint8Array[]>, cachedBitmasksSignal: Signal<Uint8Array[]>,
}> = ({ }> = ({
isLoadingSignal: [getIsLoading, setIsLoading], isLoadingSignal: [getIsLoading, setIsLoading],
options, options,
canvasSignal: [getCanvas, setCanvas],
cachedMessagesSignal: [getCachedMessages], cachedMessagesSignal: [getCachedMessages],
cachedBuffersSignal: [getCachedBuffers], cachedBuffersSignal: [getCachedBuffers],
cachedBitmasksSignal: [getCachedBitmasks], cachedBitmasksSignal: [getCachedBitmasks],
}) => { }) => {
const [getCanvas, setCanvas] = createSignal<HTMLCanvasElement>()
createEffect(async () => { createEffect(async () => {
setIsLoading(true) setIsLoading(true)
const canvas = getCanvas()! const canvas = getCanvas()!
+2 -2
View File
@@ -32,8 +32,8 @@ export const GribFile: Component<{
fetchGribBinaries(grib, getAllGribLists()).then(([messages, binaryBuffers, bitmasks]) => { fetchGribBinaries(grib, getAllGribLists()).then(([messages, binaryBuffers, bitmasks]) => {
batch(() => { batch(() => {
setCachedMessages(messages) setCachedMessages(messages)
setCachedBuffers(binaryBuffers.map(b => new Uint8Array(b))) setCachedBuffers(binaryBuffers)
setCachedBitmasks(bitmasks.map(b => new Uint8Array(b))) setCachedBitmasks(bitmasks)
}) })
}) })
.catch(err => console.warn(err.message)) .catch(err => console.warn(err.message))
+12
View File
@@ -7,6 +7,7 @@ import { ReferenceTimes } from './ReferenceTimes'
import { fetchGribList, fetchGribListStructure } from './fetchGrib' import { fetchGribList, fetchGribListStructure } from './fetchGrib'
import styles from './harmonie.module.css' import styles from './harmonie.module.css'
import { SlideShow } from './SlideShow'
export const Harmonie: Component<{}> = () => { export const Harmonie: Component<{}> = () => {
const [getFileList, setFileList] = createSignal<string[]>([]) const [getFileList, setFileList] = createSignal<string[]>([])
@@ -15,6 +16,8 @@ export const Harmonie: Component<{}> = () => {
const [getIsContour, setIsContour] = createSignal(true) const [getIsContour, setIsContour] = createSignal(true)
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 [getImgList, setImgList] = createSignal<[string, ImageBitmap][]>([])
const cachedMessagesSignal = createSignal<GribMessage[]>([]) const cachedMessagesSignal = createSignal<GribMessage[]>([])
const cachedBuffersSignal = createSignal<Uint8Array[]>([]) const cachedBuffersSignal = createSignal<Uint8Array[]>([])
@@ -62,8 +65,12 @@ export const Harmonie: Component<{}> = () => {
<input type='checkbox' checked={getIsInterpolated()} onChange={()=>setIsInterpolated(!getIsInterpolated())} /> <input type='checkbox' checked={getIsInterpolated()} onChange={()=>setIsInterpolated(!getIsInterpolated())} />
</label> </label>
<ReferenceTimes <ReferenceTimes
setIsLoading={setIsLoading}
getFileList={getFileList} getFileList={getFileList}
getGribList={getGribList} getGribList={getGribList}
getCanvas={getCanvas}
options={{ getIsCrop: getIsCrop, getIsContour: getIsContour, getIsInterpolated: getIsInterpolated }}
setImgList={setImgList}
onClick={getAllGribStructure} onClick={getAllGribStructure}
/> />
<ul class={styles.fileList}> <ul class={styles.fileList}>
@@ -82,9 +89,14 @@ export const Harmonie: Component<{}> = () => {
</ul> </ul>
</div> </div>
<div class={styles.column}> <div class={styles.column}>
<SlideShow
getCanvas={getCanvas}
getImgList={getImgList}
/>
<DrawView <DrawView
isLoadingSignal={[getIsLoading, setIsLoading]} isLoadingSignal={[getIsLoading, setIsLoading]}
options={{ getIsCrop: getIsCrop, getIsContour: getIsContour, getIsInterpolated: getIsInterpolated }} options={{ getIsCrop: getIsCrop, getIsContour: getIsContour, getIsInterpolated: getIsInterpolated }}
canvasSignal={[getCanvas, setCanvas]}
cachedMessagesSignal={cachedMessagesSignal} cachedMessagesSignal={cachedMessagesSignal}
cachedBuffersSignal={cachedBuffersSignal} cachedBuffersSignal={cachedBuffersSignal}
cachedBitmasksSignal={cachedBitmasksSignal} cachedBitmasksSignal={cachedBitmasksSignal}
+52 -3
View File
@@ -1,15 +1,27 @@
import { Accessor, Component, createSignal } from 'solid-js' import { Accessor, Component, createSignal, resetErrorBoundaries, Setter } from 'solid-js'
import { DrawOptions, GribMessage } from './interfaces'
import { fetchGribBinaries } from './fetchGrib'
import { drawGrib } from './draw/drawGrib'
import { CROP_BOUNDS } from './DrawView'
import styles from './harmonie.module.css' import styles from './harmonie.module.css'
import { GribMessage } from './interfaces'
export const ReferenceTimes: Component<{ export const ReferenceTimes: Component<{
setIsLoading: Setter<boolean>,
getFileList: Accessor<string[]>, getFileList: Accessor<string[]>,
getGribList: Accessor<GribMessage[]>, getGribList: Accessor<GribMessage[]>,
getCanvas: Accessor<HTMLCanvasElement | undefined>,
options: DrawOptions,
setImgList: Setter<[string, ImageBitmap][]>,
onClick: () => void, onClick: () => void,
}> = ({ }> = ({
setIsLoading,
getFileList, getFileList,
getGribList, getGribList,
getCanvas,
options,
setImgList,
onClick, onClick,
}) => { }) => {
const [getActiveDate, setActiveDate] = createSignal('') const [getActiveDate, setActiveDate] = createSignal('')
@@ -24,13 +36,50 @@ export const ReferenceTimes: Component<{
setActiveDate(newValue) setActiveDate(newValue)
} }
async function getImgList(dateStr: 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.meteo.discipline === 0 && g.meteo.category === 1 && g.meteo.product === 192)
// .slice(0, 5) // TODO remove this
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)
// just to be sure that draws
await new Promise(resolve => setTimeout(resolve))
const img = await createImageBitmap(canvas)
return [grib.time.forecastTime, img]
})
Promise.all(promiseList)
.then(imgList => {
imgList.sort((a, b) => a[0] > b[0] ? 1 : -1)
console.log(imgList)
setImgList(imgList)
})
.finally(() => {
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}>
{ dateList().map(dateStr => { dateList().map(dateStr =>
<li> <li>
<div onClick={() => onActiveDate(dateStr)}> <div onClick={() => onActiveDate(dateStr)}>
{ dateStr } { dateStr }
</div> </div>
<div class={styles.controls} style={{ display: getActiveDate() === dateStr ? 'block' : 'none'}}> <div
class={styles.controls}
style={{ display: getActiveDate() === dateStr ? 'block' : 'none'}}
onClick={() => getImgList(dateStr)}
>
{ {
getGribList().some(g => g.time.referenceTime === dateStr) getGribList().some(g => g.time.referenceTime === dateStr)
&& <>TODO: download all forecasts</> && <>TODO: download all forecasts</>
+30
View File
@@ -0,0 +1,30 @@
import { Accessor, Component } from 'solid-js'
import styles from './harmonie.module.css'
export const SlideShow: Component<{
getCanvas: Accessor<HTMLCanvasElement | undefined>,
getImgList: Accessor<[string, ImageBitmap][]>,
}> = ({
getCanvas,
getImgList
}) => {
function draw(img: ImageBitmap) {
const canvas = getCanvas()!
const ctx = canvas.getContext('2d')!
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.drawImage(img, 0, 0)
}
return <ul class={styles.slideShow}>
{ getImgList().map(([forecastDate, img]) =>
<li onClick={() => draw(img)}>
{ format(forecastDate) }
</li>)}
</ul>
}
function format(date: string) {
return date.slice(11, 13)
// return date
}
+6 -1
View File
@@ -27,7 +27,7 @@ export function fetchGribListStructure(): Promise<GribMessage[]> {
export function fetchGribBinaries( export function fetchGribBinaries(
grib: GribMessage, grib: GribMessage,
gribList: GribMessage[], gribList: GribMessage[],
) { ): Promise<[GribMessage[], Uint8Array[], Uint8Array[]]> {
const fileName = `harmonie_${grib.time.referenceTime}_${grib.time.forecastTime}.grib` const fileName = `harmonie_${grib.time.referenceTime}_${grib.time.forecastTime}.grib`
const bitmaskSection = grib.sections.find(section => section.id === 6) const bitmaskSection = grib.sections.find(section => section.id === 6)
const binarySection = grib.sections.find(section => section.id === 7) const binarySection = grib.sections.find(section => section.id === 7)
@@ -51,4 +51,9 @@ export function fetchGribBinaries(
if(isCalculatedHourPrecipitation(grib)) fetchPromise = fetchHourPrecipitationData(grib, gribList) if(isCalculatedHourPrecipitation(grib)) fetchPromise = fetchHourPrecipitationData(grib, gribList)
return fetchPromise return fetchPromise
.then(([messages, buffers, bitmasks]) => [
messages,
buffers.map(b => new Uint8Array(b)),
bitmasks.map(b => new Uint8Array(b)),
])
} }
+5
View File
@@ -69,3 +69,8 @@
.dateList li .controls { .dateList li .controls {
padding: 10px 3px; padding: 10px 3px;
} }
.slideShow li {
display: inline;
padding: 3px 5px;
}