autumnplot-gl 2.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,104 @@
1
+ import { isWebGL2Ctx } from "./AutumnTypes";
2
+ import { PlotComponent } from "./PlotComponent";
3
+ import { hex2rgba } from "./utils";
4
+ import { WGLProgram, WGLTexture } from "autumn-wgl";
5
+ const paintball_vertex_shader_src = `uniform mat4 u_matrix;
6
+
7
+ attribute vec2 a_pos;
8
+ attribute vec2 a_tex_coord;
9
+
10
+ varying highp vec2 v_tex_coord;
11
+
12
+ void main() {
13
+ gl_Position = u_matrix * vec4(a_pos, 0.0, 1.0);
14
+ v_tex_coord = a_tex_coord;
15
+ }`
16
+ const paintball_fragment_shader_src = `#define MAX_N_COLORS 24
17
+
18
+ varying highp vec2 v_tex_coord;
19
+
20
+ uniform sampler2D u_fill_sampler;
21
+ uniform lowp vec4 u_colors[MAX_N_COLORS];
22
+ uniform int u_num_colors;
23
+ uniform highp float u_opacity;
24
+
25
+ void main() {
26
+ highp float fill_val = texture2D(u_fill_sampler, v_tex_coord).r;
27
+
28
+ if (fill_val < 0.5) {
29
+ discard;
30
+ }
31
+
32
+ lowp vec4 color = vec4(0., 0., 0., 0.);
33
+
34
+ for (int nclr = 0; nclr < MAX_N_COLORS ; nclr++) {
35
+ if (nclr >= u_num_colors || fill_val < 0.99) { break; }
36
+
37
+ lowp float mem_active = mod(fill_val, 2.);
38
+ color = mix(color, u_colors[nclr], mem_active);
39
+ fill_val = floor(fill_val / 2.);
40
+ }
41
+
42
+ color.a = color.a * u_opacity;
43
+ gl_FragColor = color;
44
+ }`
45
+ /**
46
+ * A class representing a paintball plot, which is a plot of objects in every member of an ensemble. Objects are usually defined by a single threshold on
47
+ * a field (such as simulated reflectivity greater than 40 dBZ), but could in theory be defined by any arbitrarily complicated method. In autumnplot-gl,
48
+ * the data for the paintball plot is given as a single field with the objects from each member encoded as "bits" in the field. Because the field is made up
49
+ * of single-precision floats, this works for up to 24 members. (Technically speaking, I don't need the quotes around "bits", as they're bits of the
50
+ * significand of an IEEE 754 float.)
51
+ */
52
+ class Paintball extends PlotComponent {
53
+ /**
54
+ * Create a paintball plot
55
+ * &param field - A scalar field containing the member objects encoded as "bits." The numerical value of each grid point can be constructed like
56
+ * `1.0 * M1 + 2.0 * M2 + 4.0 * M3 + 8.0 * M4 ...`, where `M1` is 1 if that grid point is in an object in member 1 and 0 otherwise,
57
+ * `M2` is the same thing for member 2, and `M3` and `M4` and up to `Mn` are the same thing for the rest of the members.
58
+ * &param opts - Options for creating the paintball plot
59
+ */
60
+ constructor(field, opts) {
61
+ super();
62
+ this.field = field;
63
+ opts = opts !== undefined ? opts : {};
64
+ const colors = opts.colors !== undefined ? [...opts.colors] : ['#000000'];
65
+ this.colors = colors.reverse().map(color => hex2rgba(color)).flat();
66
+ this.opacity = opts.opacity !== undefined ? opts.opacity : 1.;
67
+ this.gl_elems = null;
68
+ }
69
+ /**
70
+ * &internal
71
+ * Add the paintball plot to a map.
72
+ */
73
+ async onAdd(map, gl) {
74
+ gl.getExtension('OES_texture_float');
75
+ const program = new WGLProgram(gl, paintball_vertex_shader_src, paintball_fragment_shader_src);
76
+ const { vertices: verts_buf, texcoords: tex_coords_buf } = await this.field.grid.getWGLBuffers(gl);
77
+ const vertices = verts_buf;
78
+ const texcoords = tex_coords_buf;
79
+ const format = isWebGL2Ctx(gl) ? gl.R32F : gl.LUMINANCE;
80
+ const fill_image = { 'format': format, 'type': gl.FLOAT,
81
+ 'width': this.field.grid.ni, 'height': this.field.grid.nj, 'image': this.field.data,
82
+ 'mag_filter': gl.NEAREST,
83
+ };
84
+ const fill_texture = new WGLTexture(gl, fill_image);
85
+ this.gl_elems = {
86
+ program: program, vertices: vertices, fill_texture: fill_texture, texcoords: texcoords,
87
+ };
88
+ }
89
+ /**
90
+ * &internal
91
+ * Render the paintball plot
92
+ */
93
+ render(gl, matrix) {
94
+ if (this.gl_elems === null)
95
+ return;
96
+ const gl_elems = this.gl_elems;
97
+ // Render to framebuffer
98
+ gl_elems.program.use({ 'a_pos': gl_elems.vertices, 'a_tex_coord': gl_elems.texcoords }, { 'u_matrix': matrix, 'u_opacity': this.opacity, 'u_colors': this.colors, 'u_num_colors': this.colors.length / 4 }, { 'u_fill_sampler': gl_elems.fill_texture });
99
+ gl.enable(gl.BLEND);
100
+ gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
101
+ gl_elems.program.draw();
102
+ }
103
+ }
104
+ export default Paintball;
@@ -0,0 +1,20 @@
1
+ import * as Comlink from 'comlink';
2
+ import { MapType } from './Map';
3
+ import { WebGLAnyRenderingContext } from './AutumnTypes';
4
+ declare const layer_worker: Comlink.Remote<{
5
+ makeBBElements: (field_lats: Float32Array, field_lons: Float32Array, field_ni: number, field_nj: number, thin_fac_base: number, max_zoom: number) => {
6
+ pts: Float32Array;
7
+ tex_coords: Float32Array;
8
+ };
9
+ makeDomainVerticesAndTexCoords: (field_lats: Float32Array, field_lons: Float32Array, field_ni: number, field_nj: number, texcoord_margin_r: number, texcoord_margin_s: number) => {
10
+ vertices: Float32Array;
11
+ tex_coords: Float32Array;
12
+ grid_cell_size: Float32Array;
13
+ };
14
+ makePolyLines: (lines: import("./AutumnTypes").LineSpec[]) => import("./AutumnTypes").PolylineSpec;
15
+ }>;
16
+ declare abstract class PlotComponent {
17
+ abstract onAdd(map: MapType, gl: WebGLAnyRenderingContext): Promise<void>;
18
+ abstract render(gl: WebGLAnyRenderingContext, matrix: number[]): void;
19
+ }
20
+ export { PlotComponent, layer_worker };
@@ -0,0 +1,6 @@
1
+ import * as Comlink from 'comlink';
2
+ const worker = new Worker(new URL('./PlotLayer.worker', import.meta.url));
3
+ const layer_worker = Comlink.wrap(worker);
4
+ class PlotComponent {
5
+ }
6
+ export { PlotComponent, layer_worker };
@@ -0,0 +1,96 @@
1
+ import { WebGLAnyRenderingContext } from './AutumnTypes';
2
+ import { MapType } from './Map';
3
+ import { PlotComponent } from './PlotComponent';
4
+ declare abstract class PlotLayerBase {
5
+ readonly type: 'custom';
6
+ readonly id: string;
7
+ constructor(id: string);
8
+ abstract onAdd(map: MapType, gl: WebGLAnyRenderingContext): void;
9
+ abstract render(gl: WebGLAnyRenderingContext, matrix: number[]): void;
10
+ }
11
+ /**
12
+ * A static map layer. The data are assumed to be static in time. If the data have a time component (e.g., a model forecast), an {@link MultiPlotLayer}
13
+ * may be more appropriate.
14
+ * @example
15
+ * // Create map layers from provided fields
16
+ * const height_layer = new PlotLayer('height-contours', height_contours);
17
+ * const wind_speed_layer = new PlotLayer('wind-speed-fill', wind_speed_fill);
18
+ * const barb_layer = new PlotLayer('barbs', wind_barbs);
19
+ */
20
+ declare class PlotLayer extends PlotLayerBase {
21
+ readonly field: PlotComponent;
22
+ /**
23
+ * Create a map layer from a field
24
+ * @param id - A unique id for this layer
25
+ * @param field - The field to plot in this layer
26
+ */
27
+ constructor(id: string, field: PlotComponent);
28
+ /**
29
+ * @internal
30
+ * Add this layer to a map
31
+ */
32
+ onAdd(map: MapType, gl: WebGLAnyRenderingContext): void;
33
+ /**
34
+ * @internal
35
+ * Render this layer
36
+ */
37
+ render(gl: WebGLAnyRenderingContext, matrix: number[]): void;
38
+ }
39
+ /**
40
+ * A varying map layer. If the data don't have a varying component, such as over time, it might be easier to use an {@link PlotLayer} instead.
41
+ * @example
42
+ * // Create a varying map layer
43
+ * height_layer = new MultiPlotLayer('height-contours');
44
+ *
45
+ * // Add some fields to it
46
+ * height_layer.addField(height_contour_f00, '20230112_1200');
47
+ * height_layer.addField(height_contour_f01, '20230112_1300');
48
+ * height_layer.addField(height_contour_f02, '20230112_1400');
49
+ *
50
+ * // Set the date/time in the map layer
51
+ * height_layer.setActiveKey('20230112_1200');
52
+ */
53
+ declare class MultiPlotLayer extends PlotLayerBase {
54
+ /** @private */
55
+ fields: Record<string, PlotComponent>;
56
+ /** @private */
57
+ field_key: string | null;
58
+ /** @private */
59
+ map: MapType | null;
60
+ /** @private */
61
+ gl: WebGLAnyRenderingContext | null;
62
+ /**
63
+ * Create a time-varying map layer
64
+ * @param id - A unique id for this layer
65
+ */
66
+ constructor(id: string);
67
+ /**
68
+ * @internal
69
+ * Add this layer to a map
70
+ */
71
+ onAdd(map: MapType, gl: WebGLAnyRenderingContext): void;
72
+ /**
73
+ * @internal
74
+ * Render this layer
75
+ */
76
+ render(gl: WebGLAnyRenderingContext, matrix: number[]): void;
77
+ /**
78
+ * Set the active key
79
+ * @param key - The new key
80
+ */
81
+ setActiveKey(key: string): void;
82
+ /**
83
+ * Get a list of all dates/times that have been added to the layer
84
+ * @returns An array of dates/times
85
+ */
86
+ getKeys(): string[];
87
+ /**
88
+ * Add a field valid at a specific date/time
89
+ * @param field - The field to add
90
+ * @param dt - The date/time at which the field is valid
91
+ */
92
+ addField(field: PlotComponent, key: string): void;
93
+ /** @private */
94
+ _repaintIfNecessary(old_field_key: string | null): void;
95
+ }
96
+ export { PlotLayer, MultiPlotLayer };
@@ -0,0 +1,131 @@
1
+ class PlotLayerBase {
2
+ constructor(id) {
3
+ this.type = 'custom';
4
+ this.id = id;
5
+ }
6
+ }
7
+ /**
8
+ * A static map layer. The data are assumed to be static in time. If the data have a time component (e.g., a model forecast), an {@link MultiPlotLayer}
9
+ * may be more appropriate.
10
+ * @example
11
+ * // Create map layers from provided fields
12
+ * const height_layer = new PlotLayer('height-contours', height_contours);
13
+ * const wind_speed_layer = new PlotLayer('wind-speed-fill', wind_speed_fill);
14
+ * const barb_layer = new PlotLayer('barbs', wind_barbs);
15
+ */
16
+ class PlotLayer extends PlotLayerBase {
17
+ /**
18
+ * Create a map layer from a field
19
+ * @param id - A unique id for this layer
20
+ * @param field - The field to plot in this layer
21
+ */
22
+ constructor(id, field) {
23
+ super(id);
24
+ this.field = field;
25
+ }
26
+ /**
27
+ * @internal
28
+ * Add this layer to a map
29
+ */
30
+ onAdd(map, gl) {
31
+ this.field.onAdd(map, gl);
32
+ }
33
+ /**
34
+ * @internal
35
+ * Render this layer
36
+ */
37
+ render(gl, matrix) {
38
+ this.field.render(gl, matrix);
39
+ }
40
+ }
41
+ /**
42
+ * A varying map layer. If the data don't have a varying component, such as over time, it might be easier to use an {@link PlotLayer} instead.
43
+ * @example
44
+ * // Create a varying map layer
45
+ * height_layer = new MultiPlotLayer('height-contours');
46
+ *
47
+ * // Add some fields to it
48
+ * height_layer.addField(height_contour_f00, '20230112_1200');
49
+ * height_layer.addField(height_contour_f01, '20230112_1300');
50
+ * height_layer.addField(height_contour_f02, '20230112_1400');
51
+ *
52
+ * // Set the date/time in the map layer
53
+ * height_layer.setActiveKey('20230112_1200');
54
+ */
55
+ class MultiPlotLayer extends PlotLayerBase {
56
+ /**
57
+ * Create a time-varying map layer
58
+ * @param id - A unique id for this layer
59
+ */
60
+ constructor(id) {
61
+ super(id);
62
+ this.fields = {};
63
+ this.field_key = null;
64
+ this.map = null;
65
+ this.gl = null;
66
+ }
67
+ /**
68
+ * @internal
69
+ * Add this layer to a map
70
+ */
71
+ onAdd(map, gl) {
72
+ this.map = map;
73
+ this.gl = gl;
74
+ Object.values(this.fields).forEach(field => {
75
+ field.onAdd(map, gl).then(res => {
76
+ this._repaintIfNecessary(null);
77
+ });
78
+ });
79
+ this._repaintIfNecessary(null);
80
+ }
81
+ /**
82
+ * @internal
83
+ * Render this layer
84
+ */
85
+ render(gl, matrix) {
86
+ if (this.map !== null && this.gl !== null && this.field_key !== null
87
+ && this.fields.hasOwnProperty(this.field_key) && this.fields[this.field_key] !== null) {
88
+ this.fields[this.field_key].render(gl, matrix);
89
+ }
90
+ }
91
+ /**
92
+ * Set the active key
93
+ * @param key - The new key
94
+ */
95
+ setActiveKey(key) {
96
+ const old_field_key = this.field_key;
97
+ this.field_key = key;
98
+ this._repaintIfNecessary(old_field_key);
99
+ }
100
+ /**
101
+ * Get a list of all dates/times that have been added to the layer
102
+ * @returns An array of dates/times
103
+ */
104
+ getKeys() {
105
+ return Object.keys(this.fields);
106
+ }
107
+ /**
108
+ * Add a field valid at a specific date/time
109
+ * @param field - The field to add
110
+ * @param dt - The date/time at which the field is valid
111
+ */
112
+ addField(field, key) {
113
+ const old_field_key = this.field_key;
114
+ if (this.map !== null && this.gl !== null && field !== null) {
115
+ field.onAdd(this.map, this.gl).then(res => {
116
+ this._repaintIfNecessary(null);
117
+ });
118
+ }
119
+ this.fields[key] = field;
120
+ if (this.field_key === null) {
121
+ this.field_key = key;
122
+ }
123
+ }
124
+ /** @private */
125
+ _repaintIfNecessary(old_field_key) {
126
+ if (this.map !== null && old_field_key !== this.field_key) {
127
+ this.map.triggerRepaint();
128
+ }
129
+ }
130
+ }
131
+ export { PlotLayer, MultiPlotLayer };
@@ -0,0 +1,18 @@
1
+ import { PolylineSpec, LineSpec } from "./AutumnTypes";
2
+ declare function makeBBElements(field_lats: Float32Array, field_lons: Float32Array, field_ni: number, field_nj: number, thin_fac_base: number, max_zoom: number): {
3
+ pts: Float32Array;
4
+ tex_coords: Float32Array;
5
+ };
6
+ declare function makeDomainVerticesAndTexCoords(field_lats: Float32Array, field_lons: Float32Array, field_ni: number, field_nj: number, texcoord_margin_r: number, texcoord_margin_s: number): {
7
+ vertices: Float32Array;
8
+ tex_coords: Float32Array;
9
+ grid_cell_size: Float32Array;
10
+ };
11
+ declare function makePolylines(lines: LineSpec[]): PolylineSpec;
12
+ declare const ep_interface: {
13
+ makeBBElements: typeof makeBBElements;
14
+ makeDomainVerticesAndTexCoords: typeof makeDomainVerticesAndTexCoords;
15
+ makePolyLines: typeof makePolylines;
16
+ };
17
+ type PlotLayerWorker = typeof ep_interface;
18
+ export type { PlotLayerWorker };