Skip to content

Commit

Permalink
Merge pull request #3 from tsupinie/develop
Browse files Browse the repository at this point in the history
v2.2
  • Loading branch information
tsupinie authored Nov 10, 2023
2 parents f058d29 + 2aa4acc commit 00d39cf
Show file tree
Hide file tree
Showing 30 changed files with 788 additions and 336 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ npm i autumnplot-gl
Additionally, pre-built autumnplot-gl javascript files area available [here](https://tsupinie.github.io/autumnplot-gl/dist/). Adding them to your page exposes the API via the `apgl` global variable (e.g., instead of `new PlateCarreeGrid(...)` in the examples, you'd call `new apgl.PlateCarreeGrid(...)`).

### A basic contour plot
The first step in plotting data is to create a grid. Currently, the only supported grids are PlateCarree (a.k.a. Lat/Lon) and Lambert Conformal Conic.
The first step in plotting data is to create a grid. Currently, the only supported grids are PlateCarreeGrid (a.k.a. Lat/Lon), RotatedPlateCarreeGrid, and LambertGrid (a.k.a. Lambert Conformal Conic).

```javascript
// Create a grid object that covers the continental United States
const nx = 121, ny = 61;
const grid = new PlateCarreeGrid(nx, ny, -130, 20, -65, 55);
```

Next, create a RawScalarField with the data. autumnplot-gl doesn't care about how data get to the browser, but it should end up in a Float32Array in row-major order with the first element being at the southwest corner of the grid. A future version might include support for reading from, say, a Zarr file. Once you have your data in that format, to create the raw data field:
Next, create a RawScalarField with the data. autumnplot-gl doesn't care about how data get to the browser, but it should end up in a `Float32Array` or `Float16Array` in row-major order with the first element being at the southwest corner of the grid. If you're using [zarr.js](https://github.com/gzuidhof/zarr.js/), you can use the `getRaw()` function on a `ZarrArray` to get data in the correct format. Also, `Float16Array`s are not in the Javascript standard library (for now), so for the time being, you'll need to use [this library](https://github.com/petamoriken/float16). However, the nice part about using a `Float16Array` is that your data will be stored as float16s in VRAM, so they'll take up half the space as the same data as float32s. Once you have your data in that format, to create the raw data field:

```javascript
// Create the raw data field
Expand Down Expand Up @@ -154,7 +154,7 @@ The above exmple uses map tiles from [Maptiler](https://www.maptiler.com/). Map
So, I've created some [less-detailed map tiles](https://tsupinie.github.io/autumnplot-gl/tiles/) that are small enough that they can be hosted without dedicated hardware. However the tradeoff is that they're only useful down to zoom level 8 or 9 on the map, such that the viewport is somewhere between half a US state and a few counties in size. If that's good enough for you, then these tiles could be useful.

## Conspicuous absences
A few capabilities are missing from this library as of v2.0.
A few capabilities are missing from this library as of v2.2.
* Helper functions for reading from specific data formats. For instance, I'd like to add support for reading from a zarr file.
* A whole bunch of little things that ought to be fairly straightforward like tweaking the size of the wind barbs and contour thicknesses.
* Support for contour labeling. I'd like to add it, but I'm not really sure how I'd do it with the contours as I've implemented them. Any WebGL gurus, get in touch.
Expand Down
37 changes: 24 additions & 13 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "autumnplot-gl",
"version": "2.0.0",
"version": "2.2.0",
"description": "",
"main": "lib/index.js",
"types": "lib/index.d.ts",
Expand Down Expand Up @@ -31,7 +31,8 @@
"webpack-glsl-loader": "^1.0.1"
},
"dependencies": {
"autumn-wgl": "^1.1.0",
"@petamoriken/float16": "^3.8.4",
"autumn-wgl": "^1.2.0",
"comlink": "^4.3.1"
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added public/data/mrms.202112152259.cref.bin.gz
Binary file not shown.
2 changes: 2 additions & 0 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
<title>AutumnPlotGL</title>
<script src='https://unpkg.com/[email protected]/dist/maplibre-gl.js'></script>
<link href='https://unpkg.com/[email protected]/dist/maplibre-gl.css' rel='stylesheet' />
<script src='https://unpkg.com/[email protected]/dist/pako.min.js'></script>
<script src='https://unpkg.com/@petamoriken/[email protected]/browser/float16.js'></script>
<script src="autumnplot-gl.js"></script>
<script src="main.js"></script>
<style>
Expand Down
43 changes: 35 additions & 8 deletions public/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ function makeSynthetic500mbLayers() {
}
const colormap = apgl.colormaps.pw_speed500mb;

const raw_hght_field = new apgl.RawScalarField(grid, new Float32Array(hght));
const raw_u_field = new apgl.RawScalarField(grid, new Float32Array(u));
const raw_v_field = new apgl.RawScalarField(grid, new Float32Array(v));
const arrayType = float16.Float16Array;

const raw_hght_field = new apgl.RawScalarField(grid, new arrayType(hght));
const raw_u_field = new apgl.RawScalarField(grid, new arrayType(u));
const raw_v_field = new apgl.RawScalarField(grid, new arrayType(v));

const raw_ws_field = apgl.RawScalarField.aggregateFields(Math.hypot, raw_u_field, raw_v_field);
const raw_vec_field = new apgl.RawVectorField(grid, new Float32Array(u), new Float32Array(v), {relative_to: 'grid'});
const raw_vec_field = new apgl.RawVectorField(grid, new arrayType(u), new arrayType(v), {relative_to: 'grid'});

const cntr = new apgl.Contour(raw_hght_field, {interval: 1, color: '#000000', thinner: zoom => zoom < 5 ? 2 : 1});
const filled = new apgl.ContourFill(raw_ws_field, {'cmap': colormap, 'opacity': 0.8});
Expand All @@ -38,8 +40,9 @@ function makeSynthetic500mbLayers() {
async function fetchBinary(fname) {
resp = await fetch(fname);
const blob = await resp.blob();
const ary = await blob.arrayBuffer();
return new Float32Array(ary);
const ary = new Uint8Array(await blob.arrayBuffer());
const ary_inflated = pako.inflate(ary);
return new float16.Float16Array(new Float32Array(ary_inflated.buffer));
}

async function makeHREFLayers() {
Expand All @@ -50,13 +53,13 @@ async function makeHREFLayers() {
const grid_href = new apgl.LambertGrid(nx_href, ny_href, -97.5, 38.5, [38.5, 38.5],
-nx_href * dx_href / 2, -ny_href * dy_href / 2, nx_href * dx_href / 2, ny_href * dy_href / 2);

const nh_prob_data = await fetchBinary('data/hrefv3.2023051100.f036.mxuphl5000_2000m.nh_max.086400_p99.85_0040km.bin');
const nh_prob_data = await fetchBinary('data/hrefv3.2023051100.f036.mxuphl5000_2000m.nh_max.086400_p99.85_0040km.bin.gz');
const nh_prob_field = new apgl.RawScalarField(grid_href, nh_prob_data);
const nh_prob_contour = new apgl.Contour(nh_prob_field, {'levels': [0.1, 0.3, 0.5, 0.7, 0.9], 'color': '#000000'});
const nh_prob_layer = new apgl.PlotLayer('nh_probs', nh_prob_contour);


const pb_data = await fetchBinary('data/hrefv3.2023051100.f036.mxuphl5000_2000m.086400.pb75.bin');
const pb_data = await fetchBinary('data/hrefv3.2023051100.f036.mxuphl5000_2000m.086400.pb75.bin.gz');
// If I don't draw the contours, this doesn't draw anything. Why is that?
const href_pb_colors = ['#9d4c1c', '#f2b368', '#792394', '#d99cf9', '#1e3293', '#aabee3', '#bc373b', '#f0928f', '#397d21', '#b5f0ab'];

Expand Down Expand Up @@ -97,18 +100,40 @@ function makeHodoLayers() {
return {layers: [hodo_layer]};
}

async function makeMRMSLayer() {
const grid_mrms = new apgl.PlateCarreeGrid(7000, 3500, -129.995, 20.005, -60.005, 54.995);
const data = await fetchBinary('data/mrms.202112152259.cref.bin.gz');
const raw_cref_field = new apgl.RawScalarField(grid_mrms, data);
const raster_cref = new apgl.Raster(raw_cref_field, {cmap: apgl.colormaps.nws_storm_clear_refl});
const raster_layer = new apgl.PlotLayer('mrms_cref', raster_cref);

const svg = apgl.makeColorBar(apgl.colormaps.nws_storm_clear_refl, {label: "Reflectivity (dBZ)", fontface: 'Trebuchet MS',
ticks: [-20, -10, 0, 10, 20, 30, 40, 50, 60, 70],
orientation: 'horizontal', tick_direction: 'bottom'})

return {layers: [raster_layer], colorbar: svg};
}

const views = {
'default': {
name: "Synthetic 500mb",
makeLayers: makeSynthetic500mbLayers,
maxZoom: 7,
},
'href': {
name: "HREF",
makeLayers: makeHREFLayers,
maxZoom: 7,
},
'hodo': {
name: "Hodographs",
makeLayers: makeHodoLayers,
maxZoom: 7,
},
'mrms': {
name: "MRMS",
makeLayers: makeMRMSLayer,
maxZoom: 8.5,
}
};

Expand All @@ -128,6 +153,8 @@ window.addEventListener('load', () => {

async function updateMap() {
const view = views[menu.value];
map.setMaxZoom(view.maxZoom);

const {layers, colorbar} = await view.makeLayers();

current_layers.forEach(lyr => {
Expand Down
4 changes: 2 additions & 2 deletions scripts/build_npm.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ do
filename=`echo $imp | sed 's/.*"\(.*\.glsl\)".*/\1/' | sed "s/.*'\(.*\.glsl\)'.*/\1/"`
variable=`echo $imp | tr -s ' ' | sed 's/const \(.*\) =.*/\1/' | sed 's/let \(.*\) =.*/\1/' | sed 's/var \(.*\) =.*/\1/'`

file_contents=`cat lib/$filename | sed "s%//.*%%" | tr '&' '@' | tr '\n' '^'`
file_contents=`cat lib/$filename | sed "s%//.*%%" | sed 's%&%\\\&%g' | tr '\n' '^'`
replacement="const $variable = \`$file_contents\`"

cat /tmp/file.js | sed "s%$imp%$replacement%" | tr '@' '&' | tr '^' '\n' > /tmp/file.out.js
cat /tmp/file.js | sed "s%$imp%$replacement%" | tr '^' '\n' > /tmp/file.out.js
mv /tmp/file.out.js /tmp/file.js
done

Expand Down
6 changes: 5 additions & 1 deletion src/AutumnTypes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@

import { Float16Array } from "@petamoriken/float16";

interface WindProfile {
lat: number;
lon: number;
Expand Down Expand Up @@ -42,5 +44,7 @@ function isWebGL2Ctx(gl: WebGLAnyRenderingContext) : gl is WebGL2RenderingContex
return gl.getParameter(gl.VERSION).includes('WebGL 2.0');
}

type TypedArray = Float16Array | Float32Array;

export {isWebGL2Ctx};
export type {WindProfile, BillboardSpec, PolylineSpec, LineSpec, WebGLAnyRenderingContext};
export type {WindProfile, BillboardSpec, PolylineSpec, LineSpec, WebGLAnyRenderingContext, TypedArray};
25 changes: 12 additions & 13 deletions src/Barbs.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@

import { PlotComponent, layer_worker } from "./PlotComponent";
import { PlotComponent } from "./PlotComponent";
import { BillboardCollection } from './BillboardCollection';
import { hex2rgba } from './utils';
import { RawVectorField } from "./RawField";
import { MapType } from "./Map";
import { WebGLAnyRenderingContext } from "./AutumnTypes";
import { TypedArray, WebGLAnyRenderingContext } from "./AutumnTypes";

const BARB_DIMS = {
BB_WIDTH: 85,
Expand Down Expand Up @@ -139,9 +139,9 @@ interface BarbsOptions {
thin_fac?: number;
}

interface BarbsGLElems {
interface BarbsGLElems<ArrayType extends TypedArray> {
map: MapType | null;
barb_billboards: BillboardCollection | null;
barb_billboards: BillboardCollection<ArrayType> | null;
}

/**
Expand All @@ -152,21 +152,20 @@ interface BarbsGLElems {
* const vector_field = new RawVectorField(grid, u_data, v_data);
* const barbs = new Barbs(vector_field, {color: '#000000', thin_fac: 16});
*/
class Barbs extends PlotComponent {
class Barbs<ArrayType extends TypedArray> extends PlotComponent {
/** The vector field */
readonly fields: RawVectorField;
readonly color: [number, number, number];
readonly thin_fac: number;
private readonly fields: RawVectorField<ArrayType>;
public readonly color: [number, number, number];
public readonly thin_fac: number;

/** @private */
gl_elems: BarbsGLElems | null;
private gl_elems: BarbsGLElems<ArrayType> | null;

/**
* Create a field of wind barbs
* @param fields - The vector field to plot as barbs
* @param opts - Options for creating the wind barbs
*/
constructor(fields: RawVectorField, opts: BarbsOptions) {
constructor(fields: RawVectorField<ArrayType>, opts: BarbsOptions) {
super();

this.fields = fields;
Expand All @@ -182,7 +181,7 @@ class Barbs extends PlotComponent {
* @internal
* Add the barb field to a map
*/
async onAdd(map: MapType, gl: WebGLAnyRenderingContext) {
public async onAdd(map: MapType, gl: WebGLAnyRenderingContext) {
gl.getExtension('OES_texture_float');
gl.getExtension('OES_texture_float_linear');

Expand All @@ -206,7 +205,7 @@ class Barbs extends PlotComponent {
* @internal
* Render the barb field
*/
render(gl: WebGLAnyRenderingContext, matrix: number[]) {
public render(gl: WebGLAnyRenderingContext, matrix: number[]) {
if (this.gl_elems === null) return;
const gl_elems = this.gl_elems

Expand Down
Loading

0 comments on commit 00d39cf

Please sign in to comment.