UNPKG

arso-rainfall-intensity

Version:

!!!Only for slovenia!!! Provides simple api for fetching radar images of rainfall intensity and parsing from pixels and converting pixel coordinates to pairs of latitude and longitude or vice versa.

121 lines (108 loc) 5.39 kB
import RadarImageProjection from './radar-image-projection'; import { IPixelRadarResult, IPoint, IBBox } from './types'; import { assertNotNull } from './assertions'; export type { IPixelRadarResult }; const noReadingPixelResult: IPixelRadarResult = { pixel: [0, 0, 0], value: 0, group: 0 }; export const radarRainfallColorsInfo: IPixelRadarResult[] = [ { pixel: [203, 0, 204], value: 57, group: 4 }, { pixel: [181, 3, 3], value: 54, group: 4 }, { pixel: [211, 0, 0], value: 51, group: 4 }, { pixel: [255, 62, 1], value: 48, group: 3 }, { pixel: [254, 132, 0], value: 45, group: 3 }, { pixel: [254, 198, 0], value: 42, group: 3 }, { pixel: [249, 250, 1], value: 39, group: 3 }, { pixel: [184, 250, 0], value: 36, group: 2 }, { pixel: [108, 249, 0], value: 33, group: 2 }, { pixel: [66, 235, 66], value: 30, group: 2 }, { pixel: [4, 216, 131], value: 27, group: 2 }, { pixel: [0, 220, 254], value: 24, group: 1 }, { pixel: [0, 174, 253], value: 21, group: 1 }, { pixel: [0, 120, 254], value: 18, group: 1 }, { pixel: [8, 70, 254], value: 15, group: 1 }, noReadingPixelResult ]; export const radarHailProbabilityColorsInfo: IPixelRadarResult[] = [ { pixel: [250, 0, 0], value: 3, group: 3 }, { pixel: [250, 125, 0], value: 2, group: 2 }, { pixel: [250, 225, 0], value: 1, group: 1 }, noReadingPixelResult ]; class ArsoProjection extends RadarImageProjection { public readonly interestPixelBounds: IBBox; private _imagePixelRadarMap: (IPixelRadarResult)[][] | null; private _radarColorsInfoMap: Map<string, IPixelRadarResult>; constructor(radarPixelColorsInfo: IPixelRadarResult[]) { const degreeProj = 'EPSG:900913'; const meterProj = 'EPSG:4326'; // bbox from arso is wrongly aligned or proj is not totally correct // causing offset, which is fixed with those manually polished points. const bboxSW: IPoint = { x: 12.10, y: 44.657 }; const bboxNE: IPoint = { x: 17.44, y: 47.407 }; super(degreeProj, meterProj, bboxSW, bboxNE); // arso decided to color the red bbox the same color scheme // that matches radar results in pixels. So values on the border // or near it get interpolated and can match color scheme // but most definitely the rgb[211,0,0] is matched always. // mostly it can be avoided by looking into alpha channel. // as radar pixel values have alpha set to 255. but this grid also // has lines with alpha set to 255. therefor the result must // always check if colors match radar colors. this.interestPixelBounds = { x1: 25, x2: 774, y1: 12, y2: 585 }; this._radarColorsInfoMap = new Map(radarPixelColorsInfo .map((colors) => [`${colors.pixel[0]}-${colors.pixel[1]}-${colors.pixel[2]}`, colors])); this._imagePixelRadarMap = null; } loadImageFromBuffer(buffer: Buffer): void { super.loadImageFromBuffer(buffer); this._preprocessLoadedImage(); } isPixelInInterestBounds({ x, y }: IPoint): boolean { const { x1, y1, x2, y2 } = this.interestPixelBounds; return x1 <= x && x <= x2 && y1 <= y && y <= y2; } getPixelRadarValue({ x, y }: IPoint): IPixelRadarResult { assertNotNull(this._imagePixelRadarMap, 'Image not loaded.'); return this._imagePixelRadarMap[y][x]; } private _mapPixelLocationToRadarValue({ x, y }: IPoint): IPixelRadarResult { const { r, g, b, a } = this.getPixelInfo({ x, y }); if (a !== 255) { return noReadingPixelResult; } const colorResult = this._radarColorsInfoMap.get(`${r}-${g}-${b}`); if (!colorResult) { return noReadingPixelResult; } return colorResult; } private _preprocessLoadedImage(): void { assertNotNull(this.image, 'Image not loaded.'); const { width, height, data } = this.image; // Remove pixels that are outside area of interest or // if they contain unknown values. // Then create map that maps pixel to radar read value this._imagePixelRadarMap = new Array(width).fill(null) .map(() => new Array(height).fill(null)); for (let y = 0; y < height; y++) { for (let x = 0; x < width; x++) { const idx = (width * y + x) << 2; const pixelValue = this._mapPixelLocationToRadarValue({ x, y }); const pixelOutOfBounds = !this.isPixelInInterestBounds({ x, y }); const pixelUnknownValue = !pixelValue; const isIrrelevant = pixelOutOfBounds || pixelUnknownValue; this._imagePixelRadarMap[y][x] = pixelValue; if (isIrrelevant) { data[idx] = 0; data[idx + 1] = 0; data[idx + 2] = 0; data[idx + 3] = 0; } } } } } export default class ArsoRainfallProjection extends ArsoProjection { constructor() { super(radarRainfallColorsInfo); } } export class ArsoHailProbabilityProjection extends ArsoProjection { constructor() { super(radarHailProbabilityColorsInfo); } }