autumnplot-gl 3.2.0 → 4.0.0

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.
Files changed (59) hide show
  1. package/README.md +11 -207
  2. package/dist/983.autumnplot-gl.js +2 -0
  3. package/dist/983.autumnplot-gl.js.map +1 -0
  4. package/dist/autumnplot-gl.js +1 -1
  5. package/dist/autumnplot-gl.js.map +1 -1
  6. package/dist/marchingsquares.wasm +0 -0
  7. package/lib/AutumnTypes.d.ts +85 -6
  8. package/lib/AutumnTypes.js +28 -1
  9. package/lib/Barbs.d.ts +7 -5
  10. package/lib/BillboardCollection.d.ts +8 -7
  11. package/lib/BillboardCollection.js +69 -58
  12. package/lib/Color.d.ts +2 -0
  13. package/lib/Color.js +4 -0
  14. package/lib/ColorBar.d.ts +19 -0
  15. package/lib/ColorBar.js +9 -6
  16. package/lib/Colormap.d.ts +1 -0
  17. package/lib/Colormap.js +8 -8
  18. package/lib/Contour.d.ts +28 -8
  19. package/lib/Contour.js +27 -54
  20. package/lib/ContourCreator.d.ts +9 -1
  21. package/lib/ContourCreator.js +5 -4
  22. package/lib/Fill.d.ts +28 -14
  23. package/lib/Fill.js +97 -50
  24. package/lib/Grid.d.ts +132 -30
  25. package/lib/Grid.js +355 -99
  26. package/lib/Hodographs.d.ts +15 -8
  27. package/lib/Hodographs.js +48 -30
  28. package/lib/Map.d.ts +14 -1
  29. package/lib/Map.js +60 -4
  30. package/lib/Paintball.d.ts +7 -5
  31. package/lib/Paintball.js +36 -31
  32. package/lib/ParticleTracer.d.ts +19 -0
  33. package/lib/ParticleTracer.js +37 -0
  34. package/lib/PlotComponent.d.ts +7 -7
  35. package/lib/PlotComponent.js +9 -3
  36. package/lib/PlotLayer.d.ts +5 -5
  37. package/lib/PlotLayer.js +2 -2
  38. package/lib/PlotLayer.worker.d.ts +1 -2
  39. package/lib/PlotLayer.worker.js +22 -51
  40. package/lib/PolylineCollection.d.ts +5 -3
  41. package/lib/PolylineCollection.js +60 -37
  42. package/lib/RawField.d.ts +78 -23
  43. package/lib/RawField.js +147 -31
  44. package/lib/ShaderManager.d.ts +12 -0
  45. package/lib/ShaderManager.js +58 -0
  46. package/lib/StationPlot.d.ts +187 -25
  47. package/lib/StationPlot.js +209 -60
  48. package/lib/TextCollection.d.ts +9 -6
  49. package/lib/TextCollection.js +97 -62
  50. package/lib/cpp/marchingsquares.js +483 -585
  51. package/lib/cpp/marchingsquares.wasm +0 -0
  52. package/lib/cpp/marchingsquares_embind.d.ts +23 -3
  53. package/lib/index.d.ts +12 -5
  54. package/lib/index.js +8 -5
  55. package/lib/utils.d.ts +4 -1
  56. package/lib/utils.js +12 -1
  57. package/package.json +3 -3
  58. package/dist/110.autumnplot-gl.js +0 -2
  59. package/dist/110.autumnplot-gl.js.map +0 -1
package/lib/Contour.d.ts CHANGED
@@ -1,9 +1,11 @@
1
- import { TypedArray, WebGLAnyRenderingContext } from './AutumnTypes';
1
+ import { RenderMethodArg, TypedArray, WebGLAnyRenderingContext } from './AutumnTypes';
2
2
  import { MapLikeType } from './Map';
3
3
  import { PlotComponent } from './PlotComponent';
4
4
  import { RawScalarField } from './RawField';
5
5
  import { LineStyle } from './PolylineCollection';
6
6
  import { ColorMap } from './Colormap';
7
+ import { StructuredGrid } from './Grid';
8
+ /** Options for {@link Contour} components */
7
9
  interface ContourOptions {
8
10
  /**
9
11
  * The color of the contours as a hex color string
@@ -39,6 +41,10 @@ interface ContourOptions {
39
41
  * @default '-'
40
42
  */
41
43
  line_style?: LineStyle | ((level: number) => LineStyle);
44
+ /**
45
+ *
46
+ */
47
+ quad_as_tri?: boolean;
42
48
  }
43
49
  /**
44
50
  * A field of contoured data.
@@ -47,7 +53,7 @@ interface ContourOptions {
47
53
  * // meters).
48
54
  * const contours = new Contour(height_field, {color: '#000000', interval: 30});
49
55
  */
50
- declare class Contour<ArrayType extends TypedArray, MapType extends MapLikeType> extends PlotComponent<MapType> {
56
+ declare class Contour<ArrayType extends TypedArray, GridType extends StructuredGrid, MapType extends MapLikeType> extends PlotComponent<MapType> {
51
57
  private field;
52
58
  readonly opts: Required<ContourOptions>;
53
59
  private gl_elems;
@@ -57,12 +63,12 @@ declare class Contour<ArrayType extends TypedArray, MapType extends MapLikeType>
57
63
  * @param field - The field to contour
58
64
  * @param opts - Options for creating the contours
59
65
  */
60
- constructor(field: RawScalarField<ArrayType>, opts: ContourOptions);
66
+ constructor(field: RawScalarField<ArrayType, GridType>, opts: ContourOptions);
61
67
  /**
62
68
  * Update the data displayed as contours
63
69
  * @param field - The new field to contour
64
70
  */
65
- updateField(field: RawScalarField<ArrayType>): Promise<void>;
71
+ updateField(field: RawScalarField<ArrayType, GridType>): Promise<void>;
66
72
  getContours(): Promise<import("./AutumnTypes").ContourData>;
67
73
  /**
68
74
  * @internal
@@ -73,8 +79,9 @@ declare class Contour<ArrayType extends TypedArray, MapType extends MapLikeType>
73
79
  * @internal
74
80
  * Render the contours
75
81
  */
76
- render(gl: WebGLAnyRenderingContext, matrix: number[] | Float32Array): void;
82
+ render(gl: WebGLAnyRenderingContext, arg: RenderMethodArg): void;
77
83
  }
84
+ /** Options for {@link ContourLabels} */
78
85
  interface ContourLabelOptions {
79
86
  /**
80
87
  * Number of decimal places to use in the contour labels
@@ -110,13 +117,26 @@ interface ContourLabelOptions {
110
117
  * @default false
111
118
  */
112
119
  halo?: boolean;
120
+ /**
121
+ * Label density. 2 makes the labels twice as dense, 0.5 makes them half as dense.
122
+ * @default 1
123
+ */
124
+ density?: number;
113
125
  }
114
- declare class ContourLabels<ArrayType extends TypedArray, MapType extends MapLikeType> extends PlotComponent<MapType> {
126
+ /**
127
+ * Label the contours on a plot
128
+ * @example
129
+ * // Contour some data
130
+ * const contours = new Contour(height_field, {color: '#000000', interval: 30});
131
+ * // Label the contours
132
+ * const labels = new ContourLabels(contours, {text_color: '#ffffff', halo: true});
133
+ */
134
+ declare class ContourLabels<ArrayType extends TypedArray, GridType extends StructuredGrid, MapType extends MapLikeType> extends PlotComponent<MapType> {
115
135
  private readonly contours;
116
136
  private gl_elems;
117
137
  private text_collection;
118
138
  private readonly opts;
119
- constructor(contours: Contour<ArrayType, MapType>, opts?: ContourLabelOptions);
139
+ constructor(contours: Contour<ArrayType, GridType, MapType>, opts?: ContourLabelOptions);
120
140
  /**
121
141
  * Update contour labels when the field for the associated Contour object has been changed.
122
142
  */
@@ -130,7 +150,7 @@ declare class ContourLabels<ArrayType extends TypedArray, MapType extends MapLik
130
150
  * @internal
131
151
  * Render the contour labels
132
152
  */
133
- render(gl: WebGLAnyRenderingContext, matrix: number[]): void;
153
+ render(gl: WebGLAnyRenderingContext, arg: RenderMethodArg): void;
134
154
  }
135
155
  export default Contour;
136
156
  export { ContourLabels };
package/lib/Contour.js CHANGED
@@ -4,14 +4,15 @@ import { PolylineCollection, isLineStyle } from './PolylineCollection';
4
4
  import { TextCollection } from './TextCollection';
5
5
  import { Color } from './Color';
6
6
  import { normalizeOptions } from './utils';
7
- import { kdTree } from 'kd-tree-javascript';
7
+ import { UnstructuredGrid } from './Grid';
8
8
  const contour_opt_defaults = {
9
9
  color: '#000000',
10
10
  cmap: null,
11
11
  interval: 1,
12
12
  levels: null,
13
13
  line_width: 2,
14
- line_style: '-'
14
+ line_style: '-',
15
+ quad_as_tri: false
15
16
  };
16
17
  /**
17
18
  * A field of contoured data.
@@ -87,7 +88,7 @@ class Contour extends PlotComponent {
87
88
  }
88
89
  async getContours() {
89
90
  const levels = this.opts.levels === null ? undefined : this.opts.levels;
90
- return await this.field.getContours({ interval: this.opts.interval, levels: levels });
91
+ return await this.field.getContours({ interval: this.opts.interval, levels: levels, quad_as_tri: this.opts.quad_as_tri });
91
92
  }
92
93
  /**
93
94
  * @internal
@@ -103,18 +104,16 @@ class Contour extends PlotComponent {
103
104
  * @internal
104
105
  * Render the contours
105
106
  */
106
- render(gl, matrix) {
107
+ render(gl, arg) {
107
108
  if (this.gl_elems === null || this.contours === null)
108
109
  return;
109
110
  const gl_elems = this.gl_elems;
110
- if (matrix instanceof Float32Array)
111
- matrix = [...matrix];
112
111
  const zoom = gl_elems.map.getZoom();
113
112
  const map_width = gl_elems.map.getCanvas().width;
114
113
  const map_height = gl_elems.map.getCanvas().height;
115
114
  const bearing = gl_elems.map.getBearing();
116
115
  const pitch = gl_elems.map.getPitch();
117
- this.contours.forEach(cnt => cnt.render(gl, matrix, [map_width, map_height], zoom, bearing, pitch));
116
+ this.contours.forEach(cnt => cnt.render(gl, arg, [map_width, map_height], zoom, bearing, pitch));
118
117
  }
119
118
  }
120
119
  const contour_label_opt_defaults = {
@@ -124,8 +123,17 @@ const contour_label_opt_defaults = {
124
123
  font_url_template: '',
125
124
  text_color: '#000000',
126
125
  halo_color: '#000000',
127
- halo: false
126
+ halo: false,
127
+ density: 1
128
128
  };
129
+ /**
130
+ * Label the contours on a plot
131
+ * @example
132
+ * // Contour some data
133
+ * const contours = new Contour(height_field, {color: '#000000', interval: 30});
134
+ * // Label the contours
135
+ * const labels = new ContourLabels(contours, {text_color: '#ffffff', halo: true});
136
+ */
129
137
  class ContourLabels extends PlotComponent {
130
138
  constructor(contours, opts) {
131
139
  super();
@@ -146,14 +154,13 @@ class ContourLabels extends PlotComponent {
146
154
  const font_url_template = this.opts.font_url_template == '' ? map_style.glyphs : this.opts.font_url_template;
147
155
  if (font_url_template === undefined)
148
156
  throw "The map style doesn't have any glyph information. Please pass the font_url_template option to ContourLabels";
149
- const font_url = font_url_template.replace('{range}', '0-255').replace('{fontstack}', this.opts.font_face);
157
+ const font_url = font_url_template.replace('{fontstack}', this.opts.font_face);
150
158
  const label_pos = [];
151
159
  const contour_data = await this.contours.getContours();
152
160
  const contour_levels = Object.keys(contour_data).map(parseFloat);
153
161
  contour_levels.sort((a, b) => a - b);
154
162
  const map_max_zoom = map.getMaxZoom();
155
- const contour_label_spacing = 0.01 * Math.pow(2, 7 - map_max_zoom);
156
- let min_label_lat = null, max_label_lat = null, min_label_lon = null, max_label_lon = null;
163
+ const contour_label_spacing = 0.006 / this.opts.density * Math.pow(2, 7 - map_max_zoom);
157
164
  Object.entries(contour_data).forEach(([level, contours]) => {
158
165
  const icntr = (parseFloat(level) - contour_levels[0]);
159
166
  const level_str = level.toString();
@@ -176,63 +183,29 @@ class ContourLabels extends PlotComponent {
176
183
  let n_labels_placed = 0;
177
184
  for (let idist = 1; idist < dist.length; idist++) {
178
185
  const target_dist = contour_label_spacing * (n_labels_placed + (icntr / 2) % 1);
186
+ // This works fine when contour_label_spacing > the spacing between points along a contour, but when you allow the map to zoom in
187
+ // (and therefore contour_label_spacing gets small), dist[idist] outruns target_dist, so it doesn't put any more labels after the first.
179
188
  if (dist[idist - 1] <= target_dist && target_dist < dist[idist]) {
180
189
  const pt1 = contour[idist - 1];
181
190
  const pt2 = contour[idist];
182
191
  const alpha = (target_dist - dist[idist - 1]) / (dist[idist] - dist[idist - 1]);
183
192
  const pt_lon = (1 - alpha) * pt1[0] + alpha * pt2[0];
184
193
  const pt_lat = (1 - alpha) * pt1[1] + alpha * pt2[1];
185
- if (min_label_lon === null || pt_lon < min_label_lon)
186
- min_label_lon = pt_lon;
187
- if (max_label_lon === null || pt_lon > max_label_lon)
188
- max_label_lon = pt_lon;
189
- if (min_label_lat === null || pt_lat < min_label_lat)
190
- min_label_lat = pt_lat;
191
- if (max_label_lat === null || pt_lat > max_label_lat)
192
- max_label_lat = pt_lat;
193
- label_pos.push({ lon: pt_lon, lat: pt_lat, min_zoom: map_max_zoom, text: level_str });
194
+ label_pos.push({ coord: { lon: pt_lon, lat: pt_lat }, text: level_str });
194
195
  n_labels_placed++;
195
196
  }
196
197
  }
197
198
  });
198
199
  });
199
- const tree = new kdTree(label_pos, (a, b) => Math.hypot(a.lon - b.lon, a.lat - b.lat), ['lon', 'lat']);
200
- if (min_label_lon === null || min_label_lat === null || max_label_lon === null || max_label_lat === null) {
201
- return;
202
- }
203
- const { x: min_label_x, y: max_label_y } = new LngLat(min_label_lon, min_label_lat).toMercatorCoord();
204
- const { x: max_label_x, y: min_label_y } = new LngLat(max_label_lon, max_label_lat).toMercatorCoord();
205
- const thin_grid_width = max_label_x - min_label_x;
206
- const thin_grid_height = max_label_y - min_label_y;
207
- const ni_thin_grid = Math.round(4 * thin_grid_width / contour_label_spacing);
208
- const nj_thin_grid = Math.round(4 * thin_grid_height / contour_label_spacing);
209
- const thin_grid_xs = [];
210
- const thin_grid_ys = [];
211
- for (let idx = 0; idx < ni_thin_grid; idx++) {
212
- thin_grid_xs.push(min_label_x + (idx / ni_thin_grid) * thin_grid_width);
213
- }
214
- for (let jdy = 0; jdy < nj_thin_grid; jdy++) {
215
- thin_grid_ys.push(min_label_y + (jdy / nj_thin_grid) * thin_grid_height);
216
- }
217
- let skip = 1;
218
- for (let zoom = map_max_zoom - 1; zoom >= 0; zoom--) {
219
- for (let idx = 0; idx < ni_thin_grid; idx += skip) {
220
- for (let jdy = 0; jdy < nj_thin_grid; jdy += skip) {
221
- const grid_x = thin_grid_xs[idx];
222
- const grid_y = thin_grid_ys[jdy];
223
- const ll = LngLat.fromMercatorCoord(grid_x, grid_y);
224
- const [label, dist] = tree.nearest({ lon: ll.lng, lat: ll.lat, min_zoom: 0, text: "" }, 1)[0];
225
- label.min_zoom = zoom;
226
- }
227
- }
228
- skip *= 2;
229
- }
200
+ const label_grid = new UnstructuredGrid(label_pos.map(lp => lp.coord));
201
+ const min_zoom = label_grid.getMinVisibleZoom(4);
202
+ const text_specs = label_pos.map((lp, ilp) => ({ ...lp.coord, min_zoom: min_zoom[ilp], text: lp.text }));
230
203
  const tc_opts = {
231
204
  horizontal_align: 'center', vertical_align: 'middle', font_size: this.opts.font_size,
232
205
  halo: this.opts.halo,
233
206
  text_color: Color.fromHex(this.opts.text_color), halo_color: Color.fromHex(this.opts.halo_color),
234
207
  };
235
- this.text_collection = await TextCollection.make(gl, label_pos, font_url, tc_opts);
208
+ this.text_collection = await TextCollection.make(gl, text_specs, font_url, tc_opts);
236
209
  map.triggerRepaint();
237
210
  }
238
211
  /**
@@ -249,14 +222,14 @@ class ContourLabels extends PlotComponent {
249
222
  * @internal
250
223
  * Render the contour labels
251
224
  */
252
- render(gl, matrix) {
225
+ render(gl, arg) {
253
226
  if (this.gl_elems === null || this.text_collection === null)
254
227
  return;
255
228
  const gl_elems = this.gl_elems;
256
229
  const map_width = gl_elems.map.getCanvas().width;
257
230
  const map_height = gl_elems.map.getCanvas().height;
258
231
  const map_zoom = gl_elems.map.getZoom();
259
- this.text_collection.render(gl, matrix, [map_width, map_height], map_zoom);
232
+ this.text_collection.render(gl, arg, [map_width, map_height], map_zoom);
260
233
  }
261
234
  }
262
235
  export default Contour;
@@ -2,7 +2,11 @@ import { MarchingSquaresModule } from './cpp/marchingsquares';
2
2
  import './cpp/marchingsquares.wasm';
3
3
  import { Grid } from "./Grid";
4
4
  import { ContourData, TypedArray } from "./AutumnTypes";
5
- declare function initMSModule(): Promise<MarchingSquaresModule>;
5
+ interface InitMSModuleOpts {
6
+ document_script?: string;
7
+ }
8
+ declare function initMSModule(opts: InitMSModuleOpts): Promise<MarchingSquaresModule>;
9
+ /** Options for contouring data via {@link RawScalarField.getContours | RawScalarField.getContours()} */
6
10
  interface FieldContourOpts {
7
11
  /**
8
12
  * The interval at which to create contours. The field will be contoured at this interval from its minimum to its maximum.
@@ -12,6 +16,10 @@ interface FieldContourOpts {
12
16
  * Contour the field at these specific levels.
13
17
  */
14
18
  levels?: number[];
19
+ /**
20
+ * Add triangles in the contouring, which takes longer and generates more detailed (not necessarily smoother or better) contours
21
+ */
22
+ quad_as_tri?: boolean;
15
23
  }
16
24
  declare function contourCreator<ArrayType extends TypedArray>(data: ArrayType, grid: Grid, opts: FieldContourOpts): Promise<ContourData>;
17
25
  export { contourCreator, initMSModule };
@@ -1,9 +1,9 @@
1
1
  import Module from './cpp/marchingsquares';
2
2
  import './cpp/marchingsquares.wasm';
3
3
  let msm_promise = null;
4
- function initMSModule() {
4
+ function initMSModule(opts) {
5
5
  if (msm_promise === null) {
6
- msm_promise = Module();
6
+ msm_promise = Module({ 'locateFile': (fname, dir) => (opts.document_script === undefined ? dir : opts.document_script) + fname });
7
7
  }
8
8
  return msm_promise;
9
9
  }
@@ -12,12 +12,13 @@ async function contourCreator(data, grid, opts) {
12
12
  throw "Must supply either an interval or levels to contourCreator()";
13
13
  }
14
14
  const interval = opts.interval === undefined ? 0 : opts.interval;
15
- const msm = await initMSModule();
15
+ const quad_as_tri = opts.quad_as_tri === undefined ? false : opts.quad_as_tri;
16
+ const msm = await initMSModule({});
16
17
  const grid_coords = grid.getGridCoords();
17
18
  const getContourLevels = data instanceof Float32Array ? msm.getContourLevelsFloat32 : msm.getContourLevelsFloat16;
18
19
  const makeContours = data instanceof Float32Array ? msm.makeContoursFloat32 : msm.makeContoursFloat16;
19
20
  const levels = opts.levels === undefined ? getContourLevels(data, grid.ni, grid.nj, interval) : opts.levels;
20
- const contours = makeContours(data, grid_coords.x, grid_coords.y, levels, (x, y) => grid.transform(x, y, { inverse: true }));
21
+ const contours = makeContours(data, grid_coords.x, grid_coords.y, levels, (x, y) => grid.transform(x, y, { inverse: true }), quad_as_tri);
21
22
  return contours;
22
23
  }
23
24
  export { contourCreator, initMSModule };
package/lib/Fill.d.ts CHANGED
@@ -2,37 +2,51 @@ import { PlotComponent } from './PlotComponent';
2
2
  import { ColorMap } from './Colormap';
3
3
  import { RawScalarField } from './RawField';
4
4
  import { MapLikeType } from './Map';
5
- import { TypedArray, WebGLAnyRenderingContext } from './AutumnTypes';
5
+ import { RenderMethodArg, TypedArray, WebGLAnyRenderingContext } from './AutumnTypes';
6
+ import { StructuredGrid } from './Grid';
7
+ /** Options for {@link ContourFill} components */
6
8
  interface ContourFillOptions {
7
- /** The color map to use when creating the fills */
8
- cmap: ColorMap;
9
+ /** The color maps to use when creating the fills */
10
+ cmap: ColorMap | ColorMap[];
11
+ /**
12
+ * A mask specifying where to use each color map. This should be on the same grid as the RawScalarField passed.
13
+ * A 1 in the mask means to use the first colormap, a 2 means to use the second colormap, etc.
14
+ */
15
+ cmap_mask?: Uint8Array | null;
9
16
  /**
10
17
  * The opacity for the filled contours
11
18
  * @default 1
12
19
  */
13
20
  opacity?: number;
14
21
  }
22
+ /** Options for {@link Raster} components */
15
23
  interface RasterOptions {
16
24
  /** The color map to use when creating the raster plot */
17
- cmap: ColorMap;
25
+ cmap: ColorMap | ColorMap[];
26
+ /**
27
+ * A mask specifying where to use each color map. This should be on the same grid as the RawScalarField passed.
28
+ * A 1 in the mask means to use the first colormap, a 2 means to use the second colormap, etc.
29
+ */
30
+ cmap_mask?: Uint8Array | null;
18
31
  /**
19
32
  * The opacity for the raster plot
20
33
  * @default 1
21
34
  */
22
35
  opacity?: number;
23
36
  }
24
- declare class PlotComponentFill<ArrayType extends TypedArray, MapType extends MapLikeType> extends PlotComponent<MapType> {
37
+ declare class PlotComponentFill<ArrayType extends TypedArray, GridType extends StructuredGrid, MapType extends MapLikeType> extends PlotComponent<MapType> {
25
38
  private field;
26
39
  readonly opts: Required<ContourFillOptions>;
27
40
  private readonly cmap_gpu;
28
41
  private gl_elems;
29
42
  private fill_texture;
43
+ private mask_texture;
30
44
  protected image_mag_filter: number | null;
31
45
  protected cmap_mag_filter: number | null;
32
- constructor(field: RawScalarField<ArrayType>, opts: ContourFillOptions);
33
- updateField(field: RawScalarField<ArrayType>): Promise<void>;
46
+ constructor(field: RawScalarField<ArrayType, GridType>, opts: ContourFillOptions);
47
+ updateField(field: RawScalarField<ArrayType, GridType>, mask?: Uint8Array): Promise<void>;
34
48
  onAdd(map: MapType, gl: WebGLAnyRenderingContext): Promise<void>;
35
- render(gl: WebGLAnyRenderingContext, matrix: number[] | Float32Array): void;
49
+ render(gl: WebGLAnyRenderingContext, arg: RenderMethodArg): void;
36
50
  }
37
51
  /**
38
52
  * A raster (i.e. pixel) plot
@@ -40,18 +54,18 @@ declare class PlotComponentFill<ArrayType extends TypedArray, MapType extends Ma
40
54
  * // Create a raster plot with the provided color map
41
55
  * const raster = new Raster(wind_speed_field, {cmap: color_map});
42
56
  */
43
- declare class Raster<ArrayType extends TypedArray, MapType extends MapLikeType> extends PlotComponentFill<ArrayType, MapType> {
57
+ declare class Raster<ArrayType extends TypedArray, GridType extends StructuredGrid, MapType extends MapLikeType> extends PlotComponentFill<ArrayType, GridType, MapType> {
44
58
  /**
45
59
  * Create a raster plot
46
60
  * @param field - The field to create the raster plot from
47
61
  * @param opts - Options for creating the raster plot
48
62
  */
49
- constructor(field: RawScalarField<ArrayType>, opts: RasterOptions);
63
+ constructor(field: RawScalarField<ArrayType, GridType>, opts: RasterOptions);
50
64
  /**
51
65
  * Update the data displayed as a raster plot
52
66
  * @param field - The new field to display as a raster plot
53
67
  */
54
- updateField(field: RawScalarField<ArrayType>): Promise<void>;
68
+ updateField(field: RawScalarField<ArrayType, GridType>, mask?: Uint8Array): Promise<void>;
55
69
  /**
56
70
  * @internal
57
71
  * Add the raster plot to a map
@@ -69,18 +83,18 @@ declare class Raster<ArrayType extends TypedArray, MapType extends MapLikeType>
69
83
  * // Create a field of filled contours with the provided color map
70
84
  * const fill = new ContourFill(wind_speed_field, {cmap: color_map});
71
85
  */
72
- declare class ContourFill<ArrayType extends TypedArray, MapType extends MapLikeType> extends PlotComponentFill<ArrayType, MapType> {
86
+ declare class ContourFill<ArrayType extends TypedArray, GridType extends StructuredGrid, MapType extends MapLikeType> extends PlotComponentFill<ArrayType, GridType, MapType> {
73
87
  /**
74
88
  * Create a filled contoured field
75
89
  * @param field - The field to create filled contours from
76
90
  * @param opts - Options for creating the filled contours
77
91
  */
78
- constructor(field: RawScalarField<ArrayType>, opts: ContourFillOptions);
92
+ constructor(field: RawScalarField<ArrayType, GridType>, opts: ContourFillOptions);
79
93
  /**
80
94
  * Update the data displayed as filled contours
81
95
  * @param field - The new field to display as filled contours
82
96
  */
83
- updateField(field: RawScalarField<ArrayType>): Promise<void>;
97
+ updateField(field: RawScalarField<ArrayType, GridType>, mask?: Uint8Array): Promise<void>;
84
98
  /**
85
99
  * @internal
86
100
  * Add the filled contours to a map
package/lib/Fill.js CHANGED
@@ -1,49 +1,70 @@
1
- import { PlotComponent, getGLFormatTypeAlignment } from './PlotComponent';
1
+ import { PlotComponent } from './PlotComponent';
2
2
  import { ColorMap, ColorMapGPUInterface } from './Colormap';
3
- import { WGLProgram, WGLTexture } from 'autumn-wgl';
3
+ import { WGLTexture } from 'autumn-wgl';
4
+ import { RawScalarField } from './RawField';
5
+ import { getRendererData } from './AutumnTypes';
4
6
  import { normalizeOptions } from './utils';
5
- const contourfill_vertex_shader_src = `uniform mat4 u_matrix;
7
+ import { ShaderProgramManager } from './ShaderManager';
8
+ const contourfill_vertex_shader_src = `#version 300 es
9
+
6
10
  uniform int u_offset;
7
11
 
8
- attribute vec2 a_pos;
9
- attribute vec2 a_tex_coord;
12
+ in vec2 a_pos;
13
+ in vec2 a_tex_coord;
10
14
 
11
- varying highp vec2 v_tex_coord;
15
+ out highp vec2 v_tex_coord;
12
16
 
13
17
  void main() {
14
18
  float globe_width = 1.;
15
19
  vec2 globe_offset = vec2(globe_width * float(u_offset), 0.);
16
20
 
17
- gl_Position = u_matrix * vec4(a_pos + globe_offset, 0.0, 1.0);
21
+ gl_Position = projectTile(a_pos.xy + globe_offset);
18
22
  v_tex_coord = a_tex_coord;
19
23
  }`
20
- const contourfill_fragment_shader_src = `varying highp vec2 v_tex_coord;
24
+ const contourfill_fragment_shader_src = `#version 300 es
25
+
26
+ in highp vec2 v_tex_coord;
21
27
 
22
28
  uniform sampler2D u_fill_sampler;
29
+
30
+ #ifdef MASK
31
+ uniform sampler2D u_mask_sampler;
32
+ #endif
33
+
23
34
  uniform highp float u_opacity;
35
+ #ifdef MASK
36
+ uniform int u_mask_val;
37
+ #endif
24
38
 
25
- bool isnan(highp float val) {
26
- return ( val < 0.0 || 0.0 < val || val == 0.0 ) ? false : true;
27
- }
39
+ out highp vec4 fragColor;
28
40
 
29
41
  void main() {
30
- highp float fill_val = texture2D(u_fill_sampler, v_tex_coord).r;
42
+ highp float fill_val = texture(u_fill_sampler, v_tex_coord).r;
43
+
44
+ int draw_mask = 1;
45
+
46
+ #ifdef MASK
47
+ highp float mask_val = texture(u_mask_sampler, v_tex_coord).r;
48
+ draw_mask = int(mask_val * 255.0) == u_mask_val ? 1 : 0;
49
+ #endif
31
50
 
32
- if (isnan(fill_val)) {
51
+ if (isnan(fill_val) || draw_mask == 0) {
33
52
  discard;
34
53
  }
35
54
 
36
55
  lowp vec4 color = apply_colormap(fill_val);
37
56
  color.a = color.a * u_opacity;
38
- gl_FragColor = color;
57
+ fragColor = color;
39
58
  }`
40
59
  const default_cmap = new ColorMap([0, 1], ['#000000'], { overflow_color: '#000000', underflow_color: '#000000' });
41
60
  const contour_fill_opt_defaults = {
42
- cmap: default_cmap,
61
+ cmap: [default_cmap],
62
+ cmap_mask: null,
43
63
  opacity: 1,
44
64
  };
45
65
  const raster_opt_defaults = {
46
- cmap: default_cmap,
66
+ cmap: [default_cmap],
67
+ cmap_mask: null,
47
68
  opacity: 1,
48
69
  };
49
70
  class PlotComponentFill extends PlotComponent {
@@ -51,65 +72,91 @@ class PlotComponentFill extends PlotComponent {
51
72
  super();
52
73
  this.field = field;
53
74
  this.opts = normalizeOptions(opts, contour_fill_opt_defaults);
54
- this.cmap_gpu = new ColorMapGPUInterface(this.opts.cmap);
75
+ this.opts.cmap = Array.isArray(this.opts.cmap) ? this.opts.cmap : [this.opts.cmap];
76
+ this.cmap_gpu = this.opts.cmap.map(cm => new ColorMapGPUInterface(cm));
55
77
  this.gl_elems = null;
56
78
  this.fill_texture = null;
79
+ this.mask_texture = null;
57
80
  this.image_mag_filter = null;
58
81
  this.cmap_mag_filter = null;
59
82
  }
60
- async updateField(field) {
83
+ async updateField(field, mask) {
61
84
  this.field = field;
62
85
  if (this.image_mag_filter === null || this.cmap_mag_filter === null) {
63
- throw `Implement magnification filtes in a subclass`;
86
+ throw `Implement magnification filters in a subclass`;
64
87
  }
65
88
  if (this.gl_elems === null)
66
89
  return;
67
90
  const gl = this.gl_elems.gl;
68
91
  const map = this.gl_elems.map;
69
- const tex_data = this.field.getTextureData();
70
- const { format, type, row_alignment } = getGLFormatTypeAlignment(gl, !(tex_data instanceof Float32Array));
71
- const fill_image = { 'format': format, 'type': type,
72
- 'width': this.field.grid.ni, 'height': this.field.grid.nj, 'image': tex_data,
73
- 'mag_filter': this.image_mag_filter, 'row_alignment': row_alignment,
74
- };
92
+ const fill_image = this.field.getWGLTextureSpec(gl, this.image_mag_filter);
75
93
  if (this.fill_texture === null) {
76
94
  this.fill_texture = new WGLTexture(gl, fill_image);
77
95
  }
78
96
  else {
79
97
  this.fill_texture.setImageData(fill_image);
80
98
  }
99
+ if (mask !== undefined) {
100
+ if (this.opts.cmap_mask === null) {
101
+ console.warn("A mask was passed to updateField on a Fill component that didn't have a mask. The updated mask will be ignored.");
102
+ return;
103
+ }
104
+ const mask_field = new RawScalarField(this.field.grid, mask);
105
+ const mask_image = mask_field.getWGLTextureSpec(gl, gl.NEAREST);
106
+ if (this.mask_texture === null) {
107
+ this.mask_texture = new WGLTexture(gl, mask_image);
108
+ }
109
+ else {
110
+ this.mask_texture.setImageData(mask_image);
111
+ }
112
+ }
81
113
  map.triggerRepaint();
82
114
  }
83
115
  async onAdd(map, gl) {
84
116
  // Basic procedure for the filled contours inspired by https://blog.mbq.me/webgl-weather-globe/
85
- if (this.image_mag_filter === null || this.cmap_mag_filter === null) {
86
- throw `Implement magnification filtes in a subclass`;
87
- }
88
- const program = new WGLProgram(gl, contourfill_vertex_shader_src, ColorMapGPUInterface.applyShader(contourfill_fragment_shader_src));
89
117
  const { vertices: vertices, texcoords: texcoords } = await this.field.grid.getWGLBuffers(gl);
90
- this.cmap_gpu.setupShaderVariables(gl, this.cmap_mag_filter);
118
+ this.cmap_gpu.forEach(cmg => {
119
+ if (this.image_mag_filter === null || this.cmap_mag_filter === null) {
120
+ throw `Implement magnification filters in a subclass`;
121
+ }
122
+ cmg.setupShaderVariables(gl, this.cmap_mag_filter);
123
+ });
124
+ const shader_defines = [];
125
+ if (this.opts.cmap_mask !== null) {
126
+ shader_defines.push('MASK');
127
+ }
128
+ const shader_manger = new ShaderProgramManager(contourfill_vertex_shader_src, ColorMapGPUInterface.applyShader(contourfill_fragment_shader_src), shader_defines);
91
129
  this.gl_elems = {
92
- gl: gl, map: map, program: program, vertices: vertices, texcoords: texcoords,
130
+ gl: gl, shader_manager: shader_manger, map: map, vertices: vertices, texcoords: texcoords,
93
131
  };
94
- this.updateField(this.field);
132
+ this.updateField(this.field, this.opts.cmap_mask === null ? undefined : this.opts.cmap_mask);
95
133
  }
96
- render(gl, matrix) {
134
+ render(gl, arg) {
97
135
  if (this.gl_elems === null || this.fill_texture === null)
98
136
  return;
99
137
  const gl_elems = this.gl_elems;
100
- if (matrix instanceof Float32Array)
101
- matrix = [...matrix];
102
- gl_elems.program.use({ 'a_pos': gl_elems.vertices, 'a_tex_coord': gl_elems.texcoords }, { 'u_matrix': matrix, 'u_opacity': this.opts.opacity, 'u_offset': 0 }, { 'u_fill_sampler': this.fill_texture });
103
- this.cmap_gpu.bindShaderVariables(gl_elems.program);
104
- gl.enable(gl.BLEND);
105
- gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
106
- gl_elems.program.draw();
107
- gl_elems.program.setUniforms({ 'u_offset': -2 });
108
- gl_elems.program.draw();
109
- gl_elems.program.setUniforms({ 'u_offset': -1 });
110
- gl_elems.program.draw();
111
- gl_elems.program.setUniforms({ 'u_offset': 1 });
112
- gl_elems.program.draw();
138
+ const render_data = getRendererData(arg);
139
+ const program = this.gl_elems.shader_manager.getShaderProgram(gl, render_data.shaderData);
140
+ program.use({ 'a_pos': gl_elems.vertices, 'a_tex_coord': gl_elems.texcoords }, { 'u_opacity': this.opts.opacity, ...this.gl_elems.shader_manager.getShaderUniforms(render_data) }, { 'u_fill_sampler': this.fill_texture });
141
+ this.cmap_gpu.forEach((cmg, icmg) => {
142
+ program.setUniforms({ 'u_offset': 0 });
143
+ if (this.opts.cmap_mask !== null && this.mask_texture !== null) {
144
+ program.setUniforms({ 'u_mask_val': icmg + 1 });
145
+ program.bindTextures({ 'u_mask_sampler': this.mask_texture });
146
+ }
147
+ cmg.bindShaderVariables(program);
148
+ gl.enable(gl.BLEND);
149
+ gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
150
+ program.draw();
151
+ if (render_data.type != 'maplibre' || !render_data.shaderData.define.includes('GLOBE')) {
152
+ program.setUniforms({ 'u_offset': -2 });
153
+ program.draw();
154
+ program.setUniforms({ 'u_offset': -1 });
155
+ program.draw();
156
+ program.setUniforms({ 'u_offset': 1 });
157
+ program.draw();
158
+ }
159
+ });
113
160
  }
114
161
  }
115
162
  /**
@@ -131,8 +178,8 @@ class Raster extends PlotComponentFill {
131
178
  * Update the data displayed as a raster plot
132
179
  * @param field - The new field to display as a raster plot
133
180
  */
134
- async updateField(field) {
135
- await super.updateField(field);
181
+ async updateField(field, mask) {
182
+ await super.updateField(field, mask);
136
183
  }
137
184
  /**
138
185
  * @internal
@@ -170,8 +217,8 @@ class ContourFill extends PlotComponentFill {
170
217
  * Update the data displayed as filled contours
171
218
  * @param field - The new field to display as filled contours
172
219
  */
173
- async updateField(field) {
174
- await super.updateField(field);
220
+ async updateField(field, mask) {
221
+ await super.updateField(field, mask);
175
222
  }
176
223
  /**
177
224
  * @internal