diff --git a/web/src/assets/latvia_contour.webp b/web/src/assets/latvia_contour.webp new file mode 100644 index 0000000..d44ac64 Binary files /dev/null and b/web/src/assets/latvia_contour.webp differ diff --git a/web/src/harmonie/GribFile.tsx b/web/src/harmonie/GribFile.tsx index 6fe3fd8..e3f107d 100644 --- a/web/src/harmonie/GribFile.tsx +++ b/web/src/harmonie/GribFile.tsx @@ -17,6 +17,7 @@ export const GribFile: Component<{ getFileGribList: Accessor // specific reference and forecast time (in one file) getAllGribLists: Accessor getIsCrop: Accessor, + getIsContour: Accessor, onClick: (name: string) => void, }> = ({ name, @@ -25,6 +26,7 @@ export const GribFile: Component<{ getFileGribList, getAllGribLists, getIsCrop, + getIsContour, onClick, }) => { let cachedMessages: GribMessage[] = [] @@ -36,10 +38,11 @@ export const GribFile: Component<{ createEffect(async () => { setIsLoading(true) const cropBounds = getIsCrop() ? CROP_BOUNDS : undefined + const contour = getIsContour() // hack to show loading spinner await new Promise(resolve => setTimeout(resolve, 100)) if (cachedMessages.length === 0) return; - drawGrib(getCanvas()!, cachedMessages, cachedBuffers, cachedBitmasks, cropBounds) + drawGrib(getCanvas()!, cachedMessages, cachedBuffers, cachedBitmasks, cropBounds, contour) setIsLoading(false) }) @@ -72,7 +75,7 @@ export const GribFile: Component<{ cachedBuffers = binaryBuffers.map(b => new Uint8Array(b)) cachedBitmasks = bitmasks.map(b => new Uint8Array(b)) const cropBounds = getIsCrop() ? CROP_BOUNDS : undefined - drawGrib(getCanvas()!, cachedMessages, cachedBuffers, cachedBitmasks, cropBounds) + drawGrib(getCanvas()!, cachedMessages, cachedBuffers, cachedBitmasks, cropBounds, getIsContour()) }) .catch(err => console.warn(err.message)) .finally(() => setIsLoading(false)) diff --git a/web/src/harmonie/Harmonie.tsx b/web/src/harmonie/Harmonie.tsx index 64e0318..8e08e47 100644 --- a/web/src/harmonie/Harmonie.tsx +++ b/web/src/harmonie/Harmonie.tsx @@ -14,6 +14,7 @@ export const Harmonie: Component<{}> = () => { const [getCanvas, setCanvas] = createSignal() const [getIsLoading, setIsLoading] = createSignal(true) const [getIsCrop, setIsCrop] = createSignal(true) + const [getIsContour, setIsContour] = createSignal(false) const [getGribList, setGribList] = createSignal([]) fetchJson(`${apiHost}/api/show/grib-list`) @@ -56,6 +57,11 @@ export const Harmonie: Component<{}> = () => { Crop Latvia setIsCrop(!getIsCrop())} /> +   +
    {getFileList().map(fileName => = () => { getFileGribList={() => getCurrentGribList(fileName)} getAllGribLists={getGribList} getIsCrop={getIsCrop} + getIsContour={getIsContour} onClick={() => onGribMessageClick(fileName)} /> )} diff --git a/web/src/harmonie/draw/drawGrib.ts b/web/src/harmonie/draw/drawGrib.ts index bba07fc..e5ee46c 100644 --- a/web/src/harmonie/draw/drawGrib.ts +++ b/web/src/harmonie/draw/drawGrib.ts @@ -3,10 +3,15 @@ import { GribMessage, MeteoParam } from '../interfaces' import { applyBitmask } from './bitmask' import { extractFromBounds } from './bounds' import { categoricalRainColors } from './categoricalRain' -import { hourPrecipitationColors, isCalculatedHourPrecipitation, precipitationColors } from './precipitation' +import { hourPrecipitationColors, precipitationColors } from './precipitation' import { temperatureColors } from './temperature' import { isCalculatedWindDirection, windDirectionArrows, windDirectionColors, windSpeedColors } from './windDirection' +import latvia_border from '../../assets/latvia_contour.webp' +const latviaBoderImg = new Image() +latviaBoderImg.onload = () => console.log('latvia_contour loaded') +latviaBoderImg.src = latvia_border + export type CropBounds = { x: number, y: number, width: number, height: number } export function drawGrib( @@ -15,6 +20,7 @@ export function drawGrib( buffers: Uint8Array[], bitmasks: Uint8Array[], cropBounds: CropBounds | undefined, + isContour: boolean, ): void { // normally we have one message/buffer/bitmask?. special cases have multiple like wind direction const [grib] = messages @@ -56,6 +62,10 @@ export function drawGrib( ctx.drawImage(tempCanvas, 0, -canvas.height) // ctx.drawImage(tempCanvas, 0, 0) ctx.restore() + + if (isContour && !!cropBounds) { + drawContour(canvas, ctx) // draw latvia contour only on cropped image + } } const CATEGORICAL_RAIN = [0, 1, 192] @@ -137,6 +147,24 @@ function fillImageData( } } +function drawContour( + canvas: HTMLCanvasElement, + ctx: CanvasRenderingContext2D, +): void { + ctx.save() + ctx.translate(canvas.width/2 + 8, canvas.height/2 - 5) + const angle = -26 + ctx.rotate(angle * Math.PI / 180) + const scale = 5.3 + const scaledWidth = latviaBoderImg.width/scale + const scaledHeight = latviaBoderImg.height/scale + ctx.drawImage(latviaBoderImg, + -scaledWidth/2, -scaledHeight/2, + scaledWidth, scaledHeight + ) + ctx.restore() +} + function rgbHexToU8(hex: string): RGBu8 { return [