Skip to content

Commit 00d39cf

Browse files
authored
Merge pull request #3 from tsupinie/develop
v2.2
2 parents f058d29 + 2aa4acc commit 00d39cf

30 files changed

+788
-336
lines changed

Diff for: README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@ npm i autumnplot-gl
1919
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(...)`).
2020

2121
### A basic contour plot
22-
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.
22+
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).
2323

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

30-
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:
30+
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:
3131

3232
```javascript
3333
// Create the raw data field
@@ -154,7 +154,7 @@ The above exmple uses map tiles from [Maptiler](https://www.maptiler.com/). Map
154154
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.
155155

156156
## Conspicuous absences
157-
A few capabilities are missing from this library as of v2.0.
157+
A few capabilities are missing from this library as of v2.2.
158158
* Helper functions for reading from specific data formats. For instance, I'd like to add support for reading from a zarr file.
159159
* A whole bunch of little things that ought to be fairly straightforward like tweaking the size of the wind barbs and contour thicknesses.
160160
* 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.

Diff for: package-lock.json

+24-13
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: package.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "autumnplot-gl",
3-
"version": "2.0.0",
3+
"version": "2.2.0",
44
"description": "",
55
"main": "lib/index.js",
66
"types": "lib/index.d.ts",
@@ -31,7 +31,8 @@
3131
"webpack-glsl-loader": "^1.0.1"
3232
},
3333
"dependencies": {
34-
"autumn-wgl": "^1.1.0",
34+
"@petamoriken/float16": "^3.8.4",
35+
"autumn-wgl": "^1.2.0",
3536
"comlink": "^4.3.1"
3637
}
3738
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Diff for: public/data/mrms.202112152259.cref.bin.gz

2.85 MB
Binary file not shown.

Diff for: public/index.html

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
<title>AutumnPlotGL</title>
55
<script src='https://unpkg.com/[email protected]/dist/maplibre-gl.js'></script>
66
<link href='https://unpkg.com/[email protected]/dist/maplibre-gl.css' rel='stylesheet' />
7+
<script src='https://unpkg.com/[email protected]/dist/pako.min.js'></script>
8+
<script src='https://unpkg.com/@petamoriken/[email protected]/browser/float16.js'></script>
79
<script src="autumnplot-gl.js"></script>
810
<script src="main.js"></script>
911
<style>

Diff for: public/main.js

+35-8
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@ function makeSynthetic500mbLayers() {
1313
}
1414
const colormap = apgl.colormaps.pw_speed500mb;
1515

16-
const raw_hght_field = new apgl.RawScalarField(grid, new Float32Array(hght));
17-
const raw_u_field = new apgl.RawScalarField(grid, new Float32Array(u));
18-
const raw_v_field = new apgl.RawScalarField(grid, new Float32Array(v));
16+
const arrayType = float16.Float16Array;
17+
18+
const raw_hght_field = new apgl.RawScalarField(grid, new arrayType(hght));
19+
const raw_u_field = new apgl.RawScalarField(grid, new arrayType(u));
20+
const raw_v_field = new apgl.RawScalarField(grid, new arrayType(v));
1921

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

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

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

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

5861

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

@@ -97,18 +100,40 @@ function makeHodoLayers() {
97100
return {layers: [hodo_layer]};
98101
}
99102

103+
async function makeMRMSLayer() {
104+
const grid_mrms = new apgl.PlateCarreeGrid(7000, 3500, -129.995, 20.005, -60.005, 54.995);
105+
const data = await fetchBinary('data/mrms.202112152259.cref.bin.gz');
106+
const raw_cref_field = new apgl.RawScalarField(grid_mrms, data);
107+
const raster_cref = new apgl.Raster(raw_cref_field, {cmap: apgl.colormaps.nws_storm_clear_refl});
108+
const raster_layer = new apgl.PlotLayer('mrms_cref', raster_cref);
109+
110+
const svg = apgl.makeColorBar(apgl.colormaps.nws_storm_clear_refl, {label: "Reflectivity (dBZ)", fontface: 'Trebuchet MS',
111+
ticks: [-20, -10, 0, 10, 20, 30, 40, 50, 60, 70],
112+
orientation: 'horizontal', tick_direction: 'bottom'})
113+
114+
return {layers: [raster_layer], colorbar: svg};
115+
}
116+
100117
const views = {
101118
'default': {
102119
name: "Synthetic 500mb",
103120
makeLayers: makeSynthetic500mbLayers,
121+
maxZoom: 7,
104122
},
105123
'href': {
106124
name: "HREF",
107125
makeLayers: makeHREFLayers,
126+
maxZoom: 7,
108127
},
109128
'hodo': {
110129
name: "Hodographs",
111130
makeLayers: makeHodoLayers,
131+
maxZoom: 7,
132+
},
133+
'mrms': {
134+
name: "MRMS",
135+
makeLayers: makeMRMSLayer,
136+
maxZoom: 8.5,
112137
}
113138
};
114139

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

129154
async function updateMap() {
130155
const view = views[menu.value];
156+
map.setMaxZoom(view.maxZoom);
157+
131158
const {layers, colorbar} = await view.makeLayers();
132159

133160
current_layers.forEach(lyr => {

Diff for: scripts/build_npm.sh

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@ do
2828
filename=`echo $imp | sed 's/.*"\(.*\.glsl\)".*/\1/' | sed "s/.*'\(.*\.glsl\)'.*/\1/"`
2929
variable=`echo $imp | tr -s ' ' | sed 's/const \(.*\) =.*/\1/' | sed 's/let \(.*\) =.*/\1/' | sed 's/var \(.*\) =.*/\1/'`
3030

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

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

Diff for: src/AutumnTypes.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11

2+
import { Float16Array } from "@petamoriken/float16";
3+
24
interface WindProfile {
35
lat: number;
46
lon: number;
@@ -42,5 +44,7 @@ function isWebGL2Ctx(gl: WebGLAnyRenderingContext) : gl is WebGL2RenderingContex
4244
return gl.getParameter(gl.VERSION).includes('WebGL 2.0');
4345
}
4446

47+
type TypedArray = Float16Array | Float32Array;
48+
4549
export {isWebGL2Ctx};
46-
export type {WindProfile, BillboardSpec, PolylineSpec, LineSpec, WebGLAnyRenderingContext};
50+
export type {WindProfile, BillboardSpec, PolylineSpec, LineSpec, WebGLAnyRenderingContext, TypedArray};

Diff for: src/Barbs.ts

+12-13
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11

2-
import { PlotComponent, layer_worker } from "./PlotComponent";
2+
import { PlotComponent } from "./PlotComponent";
33
import { BillboardCollection } from './BillboardCollection';
44
import { hex2rgba } from './utils';
55
import { RawVectorField } from "./RawField";
66
import { MapType } from "./Map";
7-
import { WebGLAnyRenderingContext } from "./AutumnTypes";
7+
import { TypedArray, WebGLAnyRenderingContext } from "./AutumnTypes";
88

99
const BARB_DIMS = {
1010
BB_WIDTH: 85,
@@ -139,9 +139,9 @@ interface BarbsOptions {
139139
thin_fac?: number;
140140
}
141141

142-
interface BarbsGLElems {
142+
interface BarbsGLElems<ArrayType extends TypedArray> {
143143
map: MapType | null;
144-
barb_billboards: BillboardCollection | null;
144+
barb_billboards: BillboardCollection<ArrayType> | null;
145145
}
146146

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

161-
/** @private */
162-
gl_elems: BarbsGLElems | null;
161+
private gl_elems: BarbsGLElems<ArrayType> | null;
163162

164163
/**
165164
* Create a field of wind barbs
166165
* @param fields - The vector field to plot as barbs
167166
* @param opts - Options for creating the wind barbs
168167
*/
169-
constructor(fields: RawVectorField, opts: BarbsOptions) {
168+
constructor(fields: RawVectorField<ArrayType>, opts: BarbsOptions) {
170169
super();
171170

172171
this.fields = fields;
@@ -182,7 +181,7 @@ class Barbs extends PlotComponent {
182181
* @internal
183182
* Add the barb field to a map
184183
*/
185-
async onAdd(map: MapType, gl: WebGLAnyRenderingContext) {
184+
public async onAdd(map: MapType, gl: WebGLAnyRenderingContext) {
186185
gl.getExtension('OES_texture_float');
187186
gl.getExtension('OES_texture_float_linear');
188187

@@ -206,7 +205,7 @@ class Barbs extends PlotComponent {
206205
* @internal
207206
* Render the barb field
208207
*/
209-
render(gl: WebGLAnyRenderingContext, matrix: number[]) {
208+
public render(gl: WebGLAnyRenderingContext, matrix: number[]) {
210209
if (this.gl_elems === null) return;
211210
const gl_elems = this.gl_elems
212211

0 commit comments

Comments
 (0)