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