Interpolate option, also interpolate wind data
This commit is contained in:
@@ -2,7 +2,7 @@ import { Accessor, Component, createEffect, createSignal, Setter } from 'solid-j
|
||||
|
||||
import styles from './harmonie.module.css'
|
||||
import { apiHost } from '../consts'
|
||||
import { GribMessage } from './interfaces'
|
||||
import { DrawOptions, GribMessage } from './interfaces'
|
||||
import { fetchBuffer } from '../helpers/fetch'
|
||||
import { drawGrib } from './draw/drawGrib'
|
||||
import { fetchWindData, isCalculatedWindDirection } from './draw/windDirection'
|
||||
@@ -16,8 +16,7 @@ export const GribFile: Component<{
|
||||
setIsLoading: Setter<boolean>,
|
||||
getFileGribList: Accessor<GribMessage[]> // specific reference and forecast time (in one file)
|
||||
getAllGribLists: Accessor<GribMessage[]>
|
||||
getIsCrop: Accessor<boolean>,
|
||||
getIsContour: Accessor<boolean>,
|
||||
options: DrawOptions;
|
||||
onClick: (name: string) => void,
|
||||
}> = ({
|
||||
name,
|
||||
@@ -25,8 +24,7 @@ export const GribFile: Component<{
|
||||
setIsLoading,
|
||||
getFileGribList,
|
||||
getAllGribLists,
|
||||
getIsCrop,
|
||||
getIsContour,
|
||||
options,
|
||||
onClick,
|
||||
}) => {
|
||||
let cachedMessages: GribMessage[] = []
|
||||
@@ -37,12 +35,13 @@ export const GribFile: Component<{
|
||||
|
||||
createEffect(async () => {
|
||||
setIsLoading(true)
|
||||
const cropBounds = getIsCrop() ? CROP_BOUNDS : undefined
|
||||
const contour = getIsContour()
|
||||
const cropBounds = options.getIsCrop() ? CROP_BOUNDS : undefined
|
||||
const contour = options.getIsContour()
|
||||
const isInterpolated = options.getIsInterpolated()
|
||||
// hack to show loading spinner
|
||||
await new Promise(resolve => setTimeout(resolve, 100))
|
||||
if (cachedMessages.length === 0) return;
|
||||
drawGrib(getCanvas()!, cachedMessages, cachedBuffers, cachedBitmasks, cropBounds, contour)
|
||||
drawGrib(getCanvas()!, cachedMessages, cachedBuffers, cachedBitmasks, cropBounds, contour, isInterpolated)
|
||||
setIsLoading(false)
|
||||
})
|
||||
|
||||
@@ -74,8 +73,8 @@ export const GribFile: Component<{
|
||||
cachedMessages = messages
|
||||
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, getIsContour())
|
||||
const cropBounds = options.getIsCrop() ? CROP_BOUNDS : undefined
|
||||
drawGrib(getCanvas()!, cachedMessages, cachedBuffers, cachedBitmasks, cropBounds, options.getIsContour(), options.getIsInterpolated())
|
||||
})
|
||||
.catch(err => console.warn(err.message))
|
||||
.finally(() => setIsLoading(false))
|
||||
|
||||
@@ -15,6 +15,7 @@ export const Harmonie: Component<{}> = () => {
|
||||
const [getIsLoading, setIsLoading] = createSignal(true)
|
||||
const [getIsCrop, setIsCrop] = createSignal(true)
|
||||
const [getIsContour, setIsContour] = createSignal(true)
|
||||
const [getIsInterpolated, setIsInterpolated] = createSignal(true)
|
||||
const [getGribList, setGribList] = createSignal<GribMessage[]>([])
|
||||
|
||||
fetchJson(`${apiHost}/api/show/grib-list`)
|
||||
@@ -65,6 +66,11 @@ export const Harmonie: Component<{}> = () => {
|
||||
Contour
|
||||
<input type='checkbox' checked={getIsContour()} onChange={()=>setIsContour(!getIsContour())} />
|
||||
</label>
|
||||
|
||||
<label>
|
||||
Interpolate
|
||||
<input type='checkbox' checked={getIsInterpolated()} onChange={()=>setIsInterpolated(!getIsInterpolated())} />
|
||||
</label>
|
||||
<ul class={styles.fileList}>
|
||||
{getFileList().map(fileName =>
|
||||
<GribFile
|
||||
@@ -73,8 +79,7 @@ export const Harmonie: Component<{}> = () => {
|
||||
setIsLoading={setIsLoading}
|
||||
getFileGribList={() => getCurrentGribList(fileName)}
|
||||
getAllGribLists={getGribList}
|
||||
getIsCrop={getIsCrop}
|
||||
getIsContour={getIsContour}
|
||||
options={{ getIsCrop: getIsCrop, getIsContour: getIsContour, getIsInterpolated: getIsInterpolated }}
|
||||
onClick={() => onGribMessageClick(fileName)}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -26,6 +26,7 @@ export function drawGrib(
|
||||
bitmasks: Uint8Array[],
|
||||
cropBounds: CropBounds | undefined,
|
||||
isContour: boolean,
|
||||
isInterpolated: boolean,
|
||||
): void {
|
||||
// normally we have one message/buffer/bitmask?. special cases have multiple like wind direction
|
||||
const [grib] = messages
|
||||
@@ -52,7 +53,7 @@ export function drawGrib(
|
||||
const ctx = canvas.getContext('2d')!
|
||||
let imgData = ctx.createImageData(cols, rows)
|
||||
|
||||
fillImageData(imgData, messages, modifiedBuffers)
|
||||
fillImageData(imgData, messages, modifiedBuffers, isInterpolated)
|
||||
if (isCalculatedWindDirection(grib)) {
|
||||
imgData = windDirectionArrows(imgData, messages, modifiedBuffers)
|
||||
}
|
||||
@@ -90,6 +91,7 @@ function fillImageData(
|
||||
imgData: ImageData,
|
||||
messages: GribMessage[],
|
||||
buffers: Uint8Array[],
|
||||
isInterpolated: boolean,
|
||||
) {
|
||||
const [grib] = messages
|
||||
const [buffer] = buffers
|
||||
@@ -116,33 +118,33 @@ function fillImageData(
|
||||
color = categoricalRainColors(firstByte)
|
||||
}
|
||||
else if (isMeteoEqual(meteo, TOTAL_PRECIPITATION)) {
|
||||
color = precipitationColors(encodedValue, conversion)
|
||||
color = precipitationColors(encodedValue, conversion, isInterpolated)
|
||||
}
|
||||
else if (isMeteoEqual(meteo, HOUR_PRECIPITATION)) {
|
||||
const [, nowPrec, prevPrec] = buffers
|
||||
const encodedValNow = toInt(nowPrec.slice(bufferI, bufferI+bitsPerDataPoint/8))
|
||||
const encodedValPrev = toInt(prevPrec.slice(bufferI, bufferI+bitsPerDataPoint/8))
|
||||
const [, metaNow, metaPrev] = messages
|
||||
color = hourPrecipitationColors(encodedValNow, metaNow.conversion, encodedValPrev, metaPrev.conversion)
|
||||
color = hourPrecipitationColors(encodedValNow, metaNow.conversion, encodedValPrev, metaPrev.conversion, isInterpolated)
|
||||
}
|
||||
else if (isMeteoEqual(meteo, RAIN_PRECIPITATION)) {
|
||||
color = precipitationColors(encodedValue, conversion)
|
||||
color = precipitationColors(encodedValue, conversion, isInterpolated)
|
||||
}
|
||||
else if (isMeteoEqual(meteo, TEMPERATURE)) {
|
||||
color = temperatureColors(encodedValue, conversion)
|
||||
color = temperatureColors(encodedValue, conversion, isInterpolated)
|
||||
}
|
||||
else if (isMeteoEqual(meteo, WIND_DIRECTION)) {
|
||||
const [, bufferU, bufferV] = buffers
|
||||
const encodedValU = toInt(bufferU.slice(bufferI, bufferI+bitsPerDataPoint/8))
|
||||
const encodedValV = toInt(bufferV.slice(bufferI, bufferI+bitsPerDataPoint/8))
|
||||
const [, metaU, metaV] = messages // first message fake one 0-2-192
|
||||
color = windDirectionColors(encodedValU, encodedValV, metaU!.conversion, metaV!.conversion)
|
||||
color = windDirectionColors(encodedValU, encodedValV, metaU!.conversion, metaV!.conversion, isInterpolated)
|
||||
}
|
||||
else if (isMeteoEqual(meteo, WIND_SPEED)) {
|
||||
color = windSpeedColors(encodedValue, conversion)
|
||||
color = windSpeedColors(encodedValue, conversion, isInterpolated)
|
||||
}
|
||||
else if (isMeteoEqual(meteo, WIND_SPEED_GUST)) {
|
||||
color = windSpeedColors(encodedValue, conversion)
|
||||
color = windSpeedColors(encodedValue, conversion, isInterpolated)
|
||||
}
|
||||
else {
|
||||
color = interpolateColors(firstByte, fromColor, toColor)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { apiHost } from '../../consts'
|
||||
import { fetchBuffer } from '../../helpers/fetch'
|
||||
import { valueToColorInterpolated } from '../../helpers/interpolateColors'
|
||||
import { valueToColorInterpolated, valueToColorThreshold } from '../../helpers/interpolateColors'
|
||||
import { GribMessage, MeteoConversion } from '../interfaces'
|
||||
import { WIND_SPEED } from './constants'
|
||||
import { rotateWind } from './windRotate'
|
||||
@@ -122,20 +122,26 @@ export function windDirectionColors(
|
||||
encodedV: number,
|
||||
convU: MeteoConversion,
|
||||
convV: MeteoConversion,
|
||||
isInterpolated: boolean,
|
||||
) {
|
||||
const windSpeedU = (convU.reference + encodedU * Math.pow(2, convU.binaryScale)) * Math.pow(10, -convU.decimalScale)
|
||||
const windSpeedV = (convV.reference + encodedV * Math.pow(2, convV.binaryScale)) * Math.pow(10, -convV.decimalScale)
|
||||
const windSpeed = Math.sqrt(Math.pow(windSpeedU, 2) + Math.pow(windSpeedV, 2))
|
||||
|
||||
return valueToColorInterpolated(windSpeed, WIND_SPEED)
|
||||
return isInterpolated
|
||||
? valueToColorInterpolated(windSpeed, WIND_SPEED)
|
||||
: valueToColorThreshold(windSpeed, WIND_SPEED)
|
||||
}
|
||||
|
||||
export function windSpeedColors(
|
||||
encodedValue: number,
|
||||
{ reference, binaryScale, decimalScale}: MeteoConversion,
|
||||
isInterpolated: boolean,
|
||||
) {
|
||||
const windSpeed = (reference + encodedValue * Math.pow(2, binaryScale)) * Math.pow(10, -decimalScale)
|
||||
return valueToColorInterpolated(windSpeed, WIND_SPEED)
|
||||
return isInterpolated
|
||||
? valueToColorInterpolated(windSpeed, WIND_SPEED)
|
||||
: valueToColorThreshold(windSpeed, WIND_SPEED)
|
||||
}
|
||||
|
||||
export function fetchWindData(
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { Accessor } from 'solid-js'
|
||||
|
||||
export type MeteoParam = {
|
||||
discipline: number, // 0=meteo, 1=hydro, 2=land surface, 3=space products
|
||||
category: number, // 0=temperature, 1=moisture, 6=cloud, 19=atmospheric
|
||||
@@ -43,3 +45,9 @@ export type GribTime = {
|
||||
referenceTime: string,
|
||||
forecastTime: string,
|
||||
}
|
||||
|
||||
export type DrawOptions = {
|
||||
getIsCrop: Accessor<boolean>,
|
||||
getIsContour: Accessor<boolean>,
|
||||
getIsInterpolated: Accessor<boolean>,
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ColorEntry } from '../grib/draw/constants'
|
||||
import { ColorEntry } from '../harmonie/draw/constants'
|
||||
|
||||
type RGBu8 = [number, number, number]
|
||||
type RGBAu8 = [number, number, number, number]
|
||||
|
||||
Reference in New Issue
Block a user