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.
102 lines (87 loc) • 3.67 kB
text/typescript
import proj4, { InterfaceProjection } from 'proj4';
import { PNG } from "pngjs";
import { IPoint, IPixel } from './types';
import { assertNotNull } from './assertions';
export default class RadarImageProjection {
protected degreeP: InterfaceProjection;
protected meterP: InterfaceProjection;
protected mBoundSW: IPoint;
protected mBoundNE: IPoint;
protected dBoundSW: IPoint;
protected dBoundNE: IPoint;
protected image: PNG | null;
constructor(
degreeUnitProjectionName: string,
meterUnitProjectionName: string,
degBboxSW: IPoint,
degBboxNE: IPoint
) {
this.degreeP = proj4.Proj(degreeUnitProjectionName);
this.meterP = proj4.Proj(meterUnitProjectionName);
this.mBoundSW = this.projectDegrees2MeterUnits(degBboxSW);
this.mBoundNE = this.projectDegrees2MeterUnits(degBboxNE);
this.dBoundSW = degBboxSW;
this.dBoundNE = degBboxNE;
this.image = null;
}
get width(): number {
assertNotNull(this.image, 'Image not loaded.');
return this.image.width;
}
get height(): number {
assertNotNull(this.image, 'Image not loaded.');
return this.image.height;
}
loadImageFromBuffer(buffer: Buffer): void {
this.image = PNG.sync.read(buffer, { checkCRC: false });
}
getImageBuffer(): Buffer {
assertNotNull(this.image, 'Image not loaded.');
return PNG.sync.write(this.image);
}
getPixelInfo({ x, y }: IPoint): IPixel {
assertNotNull(this.image, 'Image not loaded.');
const idx = (this.image.width * y + x) << 2;
return {
r: this.image.data[idx],
g: this.image.data[idx + 1],
b: this.image.data[idx + 2],
a: this.image.data[idx + 3]
};
}
projectImagePixelToDegreeUnit({ x, y }: IPoint): IPoint {
// (0,0) = top left
const pt = this.projectImagePixelToMeterUnit({ x, y });
return this.projectMetersToDegreeUnits(pt);
}
projectImagePixelToMeterUnit({ x, y }: IPoint): IPoint {
// (0,0) = top left
assertNotNull(this.image, 'Image not loaded.');
const pxMeters = this.mBoundSW.x + (this.mBoundNE.x - this.mBoundSW.x) * x / this.image.width;
const pyMeters = this.mBoundNE.y + (this.mBoundSW.y - this.mBoundNE.y) * y / this.image.height;
return { x: pxMeters, y: pyMeters };
}
projectDegreeUnitToImagePixel({ x, y }: IPoint): IPoint {
const pt = this.projectDegrees2MeterUnits({ x, y });
return this.projectMeterUnitToImagePixel(pt);
}
projectMeterUnitToImagePixel({ x, y }: IPoint): IPoint {
assertNotNull(this.image, 'Image not loaded.');
const mInput = { x, y };
const rX = (mInput.x - this.mBoundSW.x) / (this.mBoundNE.x - this.mBoundSW.x);
const rY = (mInput.y - this.mBoundNE.y) / (this.mBoundSW.y - this.mBoundNE.y);
if (rX < 0 || rX > 1 || rY < 0 || rY > 1) {
throw new Error('given units are not inside defined bounds.');
}
return {
x: Math.min(Math.floor(rX * this.image.width), this.image.width - 1),
y: Math.min(Math.floor(rY * this.image.height), this.image.height - 1)
};
}
projectDegrees2MeterUnits({ x, y }: IPoint): IPoint {
return proj4.transform(this.degreeP, this.meterP, { x, y });
}
projectMetersToDegreeUnits({ x, y }: IPoint): IPoint {
return proj4.transform(this.meterP, this.degreeP, { x, y });
}
}