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/RawField.js CHANGED
@@ -1,8 +1,16 @@
1
1
  import { Float16Array } from "@petamoriken/float16";
2
+ import { isStormRelativeWindProfile } from "./AutumnTypes";
2
3
  import { contourCreator } from "./ContourCreator";
3
- import { Cache, zip } from "./utils";
4
- function getArrayConstructor(ary) {
5
- return ary.constructor;
4
+ import { Cache, getArrayConstructor, zip } from "./utils";
5
+ import { getGLFormatTypeAlignment } from "./PlotComponent";
6
+ function getArrayDType(ary) {
7
+ if (ary instanceof Float32Array) {
8
+ return 'float32';
9
+ }
10
+ else if (ary instanceof Uint8Array) {
11
+ return 'uint8';
12
+ }
13
+ return 'float16';
6
14
  }
7
15
  /** A class representing a raw 2D field of gridded data, such as height or u wind. */
8
16
  class RawScalarField {
@@ -25,15 +33,23 @@ class RawScalarField {
25
33
  getTextureData() {
26
34
  // Need to give float16 data as uint16s to make WebGL happy: https://github.com/petamoriken/float16/issues/105
27
35
  const raw_data = this.data;
28
- let data;
29
- if (raw_data instanceof Float32Array) {
30
- data = raw_data;
31
- }
32
- else {
33
- data = new Uint16Array(raw_data.buffer);
34
- }
36
+ const raw_data_type = getArrayDType(raw_data);
37
+ const data = (raw_data_type == 'float32' || raw_data_type == 'uint8') ? raw_data : new Uint16Array(raw_data.buffer);
35
38
  return data;
36
39
  }
40
+ getWGLTextureSpec(gl, image_mag_filter) {
41
+ const tex_data = this.getTextureData();
42
+ const { format, type, row_alignment } = getGLFormatTypeAlignment(gl, getArrayDType(this.data));
43
+ return { 'format': format, 'type': type,
44
+ 'width': this.grid.ni, 'height': this.grid.nj, 'image': tex_data,
45
+ 'mag_filter': image_mag_filter, 'row_alignment': row_alignment,
46
+ };
47
+ }
48
+ /**
49
+ * Get contour data as an object with each contour level being a separate property.
50
+ * @param opts - Options for doing the contouring
51
+ * @returns contour data as an object
52
+ */
37
53
  async getContours(opts) {
38
54
  return await this.contour_cache.getValue(opts);
39
55
  }
@@ -57,6 +73,9 @@ class RawScalarField {
57
73
  const agg_data = new arrayType(mapGenerator(zipped_args, (a) => func(...a)));
58
74
  return new RawScalarField(args[0].grid, agg_data);
59
75
  }
76
+ sampleField(lon, lat) {
77
+ return this.grid.sampleNearestGridPoint(lon, lat, this.data).sample;
78
+ }
60
79
  }
61
80
  /** A class representing a 2D gridded field of vectors */
62
81
  class RawVectorField {
@@ -73,35 +92,53 @@ class RawVectorField {
73
92
  this.v = new RawScalarField(grid, v);
74
93
  this.relative_to = opts.relative_to === undefined ? 'grid' : opts.relative_to;
75
94
  }
95
+ /** @internal */
76
96
  getTextureData() {
77
97
  // Need to give float16 data as uint16s to make WebGL happy: https://github.com/petamoriken/float16/issues/105
78
98
  const raw_u = this.u.data;
79
99
  const raw_v = this.v.data;
80
- const u = raw_u instanceof Float32Array ? raw_u : new Uint16Array(raw_u.buffer);
81
- const v = raw_v instanceof Float32Array ? raw_v : new Uint16Array(raw_v.buffer);
100
+ const u_raw_data_type = getArrayDType(raw_u);
101
+ const v_raw_data_type = getArrayDType(raw_u);
102
+ const u = (u_raw_data_type == 'float32' || u_raw_data_type == 'uint8') ? raw_u : new Uint16Array(raw_u.buffer);
103
+ const v = (v_raw_data_type == 'float32' || v_raw_data_type == 'uint8') ? raw_v : new Uint16Array(raw_v.buffer);
82
104
  return { u: u, v: v };
83
105
  }
84
- getThinnedField(thin_x, thin_y) {
85
- const new_grid = this.grid.getThinnedGrid(thin_x, thin_y);
86
- const thinGrid = (data) => {
87
- const arrayType = getArrayConstructor(data);
88
- const new_data = new arrayType(new_grid.ni * new_grid.nj);
89
- for (let i = 0; i < new_grid.ni; i++) {
90
- for (let j = 0; j < new_grid.nj; j++) {
91
- const idx_old = i * thin_x + this.grid.ni * j * thin_y;
92
- const idx = i + new_grid.ni * j;
93
- new_data[idx] = data[idx_old];
94
- }
95
- }
96
- return new_data;
106
+ getWGLTextureSpecs(gl, mag_filter) {
107
+ const { u: u_thin, v: v_thin } = this.getTextureData();
108
+ const { format, type, row_alignment } = getGLFormatTypeAlignment(gl, getArrayDType(this.u.data));
109
+ const u_image = { 'format': format, 'type': type,
110
+ 'width': this.grid.ni, 'height': this.grid.nj, 'image': u_thin,
111
+ 'mag_filter': mag_filter, 'row_alignment': row_alignment,
97
112
  };
98
- const thin_u = thinGrid(this.u.data);
99
- const thin_v = thinGrid(this.v.data);
113
+ const v_image = { 'format': format, 'type': type,
114
+ 'width': this.grid.ni, 'height': this.grid.nj, 'image': v_thin,
115
+ 'mag_filter': mag_filter, 'row_alignment': row_alignment,
116
+ };
117
+ return { u: u_image, v: v_image };
118
+ }
119
+ /** @internal */
120
+ getThinnedField(thin_fac, map_max_zoom) {
121
+ const new_grid = this.grid.getThinnedGrid(thin_fac, map_max_zoom);
122
+ const thin_u = new_grid.thinDataArray(this.grid, this.u.data);
123
+ const thin_v = new_grid.thinDataArray(this.grid, this.v.data);
100
124
  return new RawVectorField(new_grid, thin_u, thin_v, { relative_to: this.relative_to });
101
125
  }
126
+ /** @internal */
102
127
  get grid() {
103
128
  return this.u.grid;
104
129
  }
130
+ sampleField(lon, lat) {
131
+ const u_sample = this.grid.sampleNearestGridPoint(lon, lat, this.u.data);
132
+ const v_sample = this.grid.sampleNearestGridPoint(lon, lat, this.v.data);
133
+ const rot = this.relative_to == 'earth' ? 0 : this.grid.getVectorRotationAtPoint(u_sample.sample_lon, u_sample.sample_lat);
134
+ const mag = Math.hypot(u_sample.sample, v_sample.sample);
135
+ let brg = (Math.PI / 2 - Math.atan2(-v_sample.sample, -u_sample.sample) + rot) * 180 / Math.PI;
136
+ if (brg > 360)
137
+ brg -= 360;
138
+ if (brg < 0)
139
+ brg += 360;
140
+ return [brg, mag];
141
+ }
105
142
  }
106
143
  /** A class grid of wind profiles */
107
144
  class RawProfileField {
@@ -114,17 +151,96 @@ class RawProfileField {
114
151
  this.profiles = profiles;
115
152
  this.grid = grid;
116
153
  }
117
- /** Get the gridded storm motion vector field (internal method) */
154
+ /**
155
+ * @internal
156
+ * Get the gridded storm motion vector field
157
+ */
118
158
  getStormMotionGrid() {
119
159
  const profiles = this.profiles;
120
160
  const u = new Float16Array(this.grid.ni * this.grid.nj).fill(parseFloat('nan'));
121
161
  const v = new Float16Array(this.grid.ni * this.grid.nj).fill(parseFloat('nan'));
122
162
  profiles.forEach(prof => {
123
163
  const idx = prof.ilon + this.grid.ni * prof.jlat;
124
- u[idx] = prof.smu;
125
- v[idx] = prof.smv;
164
+ if (isStormRelativeWindProfile(prof)) {
165
+ u[idx] = prof.smu;
166
+ v[idx] = prof.smv;
167
+ }
168
+ else {
169
+ u[idx] = 0;
170
+ v[idx] = 0;
171
+ }
126
172
  });
127
173
  return new RawVectorField(this.grid, u, v, { relative_to: 'grid' });
128
174
  }
175
+ /** @internal */
176
+ getProfileCoords() {
177
+ const { lats, lons } = this.grid.getEarthCoords();
178
+ const prof_lats = new Float32Array(this.profiles.length);
179
+ const prof_lons = new Float32Array(this.profiles.length);
180
+ this.profiles.forEach((prof, iprof) => {
181
+ const idx = prof.ilon + prof.jlat * this.grid.ni;
182
+ prof_lats[iprof] = lats[idx];
183
+ prof_lons[iprof] = lons[idx];
184
+ });
185
+ return { lats: prof_lats, lons: prof_lons };
186
+ }
187
+ }
188
+ /** Raw observation data, given as a list of objects */
189
+ class RawObsField {
190
+ /**
191
+ * Create a field of observations
192
+ * @param grid - The grid on which the obs are defined (can be either a structured or unstructured grid)
193
+ * @param data - The observation data. Conceptually, obs are given as a list of individual observations.
194
+ */
195
+ constructor(grid, data) {
196
+ this.grid = grid;
197
+ this.data = data;
198
+ }
199
+ /**
200
+ * @internal
201
+ * Get observation element as a list of scalar numbers
202
+ */
203
+ getScalar(key) {
204
+ const field_data = this.data.map(d => d[key]);
205
+ if (!field_data.map(d => typeof d == 'number' || d === null).reduce((a, b) => a && b, true))
206
+ throw `It doesn't look like ${key} contains scalar numerical data`;
207
+ return field_data;
208
+ }
209
+ /**
210
+ * @internal
211
+ * Get observed element as a list of strings (internal method)
212
+ */
213
+ getStrings(key) {
214
+ const field_data = this.data.map(d => d[key]);
215
+ if (!field_data.map(d => typeof d == 'string' || d === null).reduce((a, b) => a && b, true))
216
+ throw `It doesn't look like ${key} contains string data`;
217
+ return field_data;
218
+ }
219
+ /**
220
+ * @internal
221
+ * Get observed element as a list of vectors (internal method)
222
+ */
223
+ getVector(key) {
224
+ const field_data = this.data.map(d => d[key]);
225
+ if (!field_data.map(d => Array.isArray(d)).reduce((a, b) => a && b, true))
226
+ throw `It doesn't look like ${key} contains vector data`;
227
+ const vector_field_data = field_data;
228
+ const vec2comp = (wspd, wdir) => {
229
+ const u = -wspd * Math.sin(wdir * Math.PI / 180);
230
+ const v = -wspd * Math.cos(wdir * Math.PI / 180);
231
+ return [u, v];
232
+ };
233
+ const u_data = new Float16Array(this.grid.ni * this.grid.nj).fill(parseFloat('nan'));
234
+ const v_data = new Float16Array(this.grid.ni * this.grid.nj).fill(parseFloat('nan'));
235
+ vector_field_data.forEach(([wspd, wdir], idat) => {
236
+ if (wspd === null || wdir === null) {
237
+ return;
238
+ }
239
+ const [u, v] = vec2comp(wspd, wdir);
240
+ u_data[idat] = u;
241
+ v_data[idat] = v;
242
+ });
243
+ return new RawVectorField(this.grid, u_data, v_data, { relative_to: 'earth' });
244
+ }
129
245
  }
130
- export { RawScalarField, RawVectorField, RawProfileField };
246
+ export { RawScalarField, RawVectorField, RawProfileField, RawObsField };
@@ -0,0 +1,12 @@
1
+ import { WGLProgram } from "autumn-wgl";
2
+ import { RenderShaderData, RendererData, WebGLAnyRenderingContext } from "./AutumnTypes";
3
+ declare class ShaderProgramManager {
4
+ readonly vertex_shader_src: string;
5
+ readonly fragment_shader_src: string;
6
+ readonly shader_defines: string[];
7
+ private readonly program_map;
8
+ constructor(vertex_shader_src: string, fragment_shader_src: string, shader_defines: string[]);
9
+ getShaderProgram(gl: WebGLAnyRenderingContext, shader_data: RenderShaderData | null): WGLProgram;
10
+ getShaderUniforms(render_data: RendererData): Record<string, number | number[]>;
11
+ }
12
+ export { ShaderProgramManager };
@@ -0,0 +1,58 @@
1
+ import { WGLProgram } from "autumn-wgl";
2
+ import { mergeShaderCode } from "./utils";
3
+ class ShaderProgramManager {
4
+ constructor(vertex_shader_src, fragment_shader_src, shader_defines) {
5
+ this.vertex_shader_src = vertex_shader_src;
6
+ this.fragment_shader_src = fragment_shader_src;
7
+ this.shader_defines = shader_defines;
8
+ this.program_map = new Map();
9
+ }
10
+ getShaderProgram(gl, shader_data) {
11
+ const shader_key = shader_data === null ? 'default' : shader_data.variantName;
12
+ const cached_program = this.program_map.get(shader_key);
13
+ if (cached_program !== undefined) {
14
+ return cached_program;
15
+ }
16
+ let vertex_src = this.vertex_shader_src;
17
+ if (shader_data !== null) {
18
+ vertex_src = mergeShaderCode(shader_data.vertexShaderPrelude + '\n' + shader_data.define, vertex_src);
19
+ }
20
+ else {
21
+ const prelude = `
22
+ uniform mat4 u_projection_matrix;
23
+
24
+ vec4 projectTile(vec2 p) {
25
+ vec4 result = u_projection_matrix * vec4(p, 0.0, 1.0);
26
+ return result;
27
+ }`;
28
+ vertex_src = mergeShaderCode(prelude, vertex_src);
29
+ }
30
+ const program = new WGLProgram(gl, vertex_src, this.fragment_shader_src, { define: this.shader_defines });
31
+ this.program_map.set(shader_key, program);
32
+ return program;
33
+ }
34
+ getShaderUniforms(render_data) {
35
+ if (render_data.type == 'maplibre') {
36
+ if (render_data.shaderData.define.includes('GLOBE')) {
37
+ return {
38
+ 'u_projection_matrix': render_data.defaultProjectionData.mainMatrix,
39
+ 'u_projection_fallback_matrix': render_data.defaultProjectionData.fallbackMatrix,
40
+ 'u_projection_tile_mercator_coords': render_data.defaultProjectionData.tileMercatorCoords,
41
+ 'u_projection_clipping_plane': render_data.defaultProjectionData.clippingPlane,
42
+ 'u_projection_transition': render_data.defaultProjectionData.projectionTransition
43
+ };
44
+ }
45
+ else {
46
+ return {
47
+ 'u_projection_matrix': render_data.defaultProjectionData.mainMatrix,
48
+ };
49
+ }
50
+ }
51
+ else {
52
+ return {
53
+ 'u_projection_matrix': render_data.mainMatrix,
54
+ };
55
+ }
56
+ }
57
+ }
58
+ export { ShaderProgramManager };
@@ -1,34 +1,196 @@
1
- import { WebGLAnyRenderingContext } from "./AutumnTypes";
1
+ import { RenderMethodArg, WebGLAnyRenderingContext } from "./AutumnTypes";
2
2
  import { MapLikeType } from "./Map";
3
3
  import { PlotComponent } from "./PlotComponent";
4
- interface EarthCoord {
5
- lon: number;
6
- lat: number;
4
+ import { Grid } from "./Grid";
5
+ import { RawObsField } from "./RawField";
6
+ /**
7
+ * Positions around the station plot at which to draw the various elements
8
+ *
9
+ * | Value | Position |
10
+ * | --------|--------------|
11
+ * | `'cl'` | center-left |
12
+ * | `'ll'` | lower-left |
13
+ * | `'lc'` | lower-center |
14
+ * | `'lr'` | lower-right |
15
+ * | `'cr'` | center-right |
16
+ * | `'ur'` | upper-right |
17
+ * | `'uc'` | upper-center |
18
+ * | `'ul'` | upper-left |
19
+ * | `'c'` | center |
20
+ */
21
+ type SPPosition = 'cl' | 'll' | 'lc' | 'lr' | 'cr' | 'ur' | 'uc' | 'ul' | 'c';
22
+ /** Configuration for numerical values on station plots */
23
+ interface SPNumberConfig {
24
+ type: 'number';
25
+ /**
26
+ * The position on the station plot at which to place the number
27
+ */
28
+ pos: SPPosition;
29
+ /**
30
+ * The color to use to draw the number
31
+ * @default '#000000'
32
+ */
33
+ color?: string;
34
+ /**
35
+ * Whether to draw a halo (outline) around the number
36
+ * @default true;
37
+ */
38
+ halo?: boolean;
39
+ /**
40
+ * The color to use for the halo (outline)
41
+ * @default '#ffffff'
42
+ */
43
+ halo_color?: string;
44
+ /**
45
+ * A function that properly formats the number for display
46
+ * @example (val) => val === null ? '' : val.toFixed(0)
47
+ * @param val - The number to format
48
+ * @returns A string containing the formatted nubmer
49
+ */
50
+ formatter?: (val: number | null) => string;
7
51
  }
8
- interface ObsRawData<ObsFieldName extends string> {
9
- coord: EarthCoord;
10
- data: Record<ObsFieldName, number>;
52
+ /** Configuration for strings on station plots */
53
+ interface SPStringConfig {
54
+ type: 'string';
55
+ /**
56
+ * The position on the station plot at which to place the string
57
+ */
58
+ pos: SPPosition;
59
+ /**
60
+ * The color to use to draw the string
61
+ * @default '#000000'
62
+ */
63
+ color?: string;
64
+ /**
65
+ * Whether to draw a halo (outline) around the string
66
+ * @default true;
67
+ */
68
+ halo?: boolean;
69
+ /**
70
+ * The color to use for the halo (outline)
71
+ * @default '#ffffff'
72
+ */
73
+ halo_color?: string;
11
74
  }
12
- type SPPositions = 'cl' | 'll' | 'lc' | 'lr' | 'cr' | 'ur' | 'uc' | 'ul';
13
- type SPDataPosition<ObsFieldName extends string> = Record<ObsFieldName, SPPositions>;
14
- declare class UnstructuredGrid {
15
- readonly coords: EarthCoord[];
16
- constructor(coords: EarthCoord[]);
17
- getZoomThinning(thin_fac: number, map_max_zoom: number): number[];
75
+ /** Configuration for wind barbs on station plots */
76
+ interface SPBarbConfig {
77
+ type: 'barb';
78
+ /**
79
+ * The color to use to draw the barb
80
+ * @default '#000000'
81
+ */
82
+ color?: string;
83
+ /**
84
+ * A multiplier for the barb size
85
+ * @default 1
86
+ */
87
+ barb_size_multipler?: number;
18
88
  }
19
- declare class ObsField<ObsFieldName extends string> {
20
- readonly grid: UnstructuredGrid;
21
- readonly data: ObsRawData<ObsFieldName>[];
22
- constructor(data: ObsRawData<ObsFieldName>[]);
89
+ /**
90
+ * Accepted symbol codes for sky cover and present weather symbols
91
+ */
92
+ type SPSymbol = ('0/8' | '1/8' | '2/8' | '3/8' | '4/8' | '5/8' | '6/8' | '7/8' | '8/8' | 'clr' | 'few' | 'sct' | 'bkn' | 'ovc' | 'obsc' | 'va' | 'fu' | 'hz' | 'du' | 'bldu' | 'sa' | 'blsa' | 'vcblsa' | 'vcbldu' | 'blpy' | 'po' | 'vcpo' | 'vcds' | 'vcss' | 'br' | 'bcbr' | 'bc' | 'mifg' | 'vcts' | 'virga' | 'vcsh' | 'ts' | 'thdr' | 'vctshz' | 'tsfzfg' | 'tsbr' | 'tsdz' | 'vctsup' | '-tsup' | 'tsup' | '+tsup' | 'sq' | 'fc' | '+fc' | 'ds' | 'ss' | 'drsa' | 'drdu' | '+ds' | '+ss' | 'drsn' | '+drsn' | '-blsn' | 'blsn' | '+blsn' | 'vcblsn' | 'vcfg' | 'bcfg' | 'prfg' | 'fg' | 'fzfg' | '-vctsdz' | '-dz' | '-dzbr' | 'vctsdz' | 'dz' | '+vctsdz' | '+dz' | '-fzdz' | '-fzdzsn' | 'fzdz' | '+fzdz' | 'fzdzsn' | '-dzra' | 'dzra' | '+dzra' | '-ra' | '-rabr' | 'ra' | 'rabr' | 'rafg' | 'vcra' | '+ra' | '-fzra' | '-fzrasn' | '-fzrabr' | '-fzrapl' | '-fzrasnpl' | 'tsfzrapl' | '-tsfzra' | 'fzra' | '+fzra' | 'fzrasn' | 'tsfzra' | '-dzsn' | '-rasn' | '-snra' | '-sndz' | 'rasn' | '+rasn' | 'snra' | 'dzsn' | 'sndz' | '+dzsn' | '+sndz' | '-sn' | '-snbr' | 'sn' | '+sn' | '-snsg' | 'sg' | '-sg' | 'ic' | '-fzdzpl' | '-fzdzplsn' | 'fzdzpl' | '-fzraplsn' | 'fzrapl' | '+fzrapl' | '-rapl' | '-rasnpl' | '-raplsn' | '+rapl' | 'rapl' | '-snpl' | 'snpl' | '-pl' | 'pl' | '-plsn' | '-plra' | 'plra' | '-pldz' | '+pl' | 'plsn' | 'plup' | '+plsn' | '-sh' | '-shra' | 'sh' | 'shra' | '+sh' | '+shra' | '-shrasn' | '-shsnra' | '+shrabr' | 'shrasn' | '+shrasn' | 'shsnra' | '+shsnra' | '-shsn' | 'shsn' | '+shsn' | '-gs' | '-shgs' | 'fzraplgs' | '-sngs' | 'gsplsn' | 'gspl' | 'plgssn' | 'gs' | 'shgs' | '+gs' | '+shgs' | '-gr' | '-shgr' | '-sngr' | 'gr' | 'shgr' | '+gr' | '+shgr' | '-tsrasn' | 'tsrasn' | '-tssnra' | 'tssnra' | '-vctsra' | '-tsra' | 'tsra' | '-tsdz' | 'vctsra' | 'tspl' | '-tssn' | '-tspl' | 'tssn' | '-vctssn' | 'vctssn' | 'tsplsn' | 'tssnpl' | '-tssnpl' | '-tsragr' | 'tsrags' | 'tsragr' | 'tsgs' | 'tsgr' | '+tsfzrapl' | '+vctsra' | '+tsra' | '+tsfzra' | '+tssn' | '+tspl' | '+tsplsn' | '+vctssn' | 'tssa' | 'tsds' | 'tsdu' | '+tsgs' | '+tsgr' | '+tsrags' | '+tsragr' | 'in' | '-up' | 'up' | '+up' | '-fzup' | 'fzup' | '+fzup');
93
+ /** Configuration for symbols on station plots */
94
+ interface SPSymbolConfig {
95
+ type: 'symbol';
96
+ /**
97
+ * The position on the station plot at which to place the symbol
98
+ */
99
+ pos: SPPosition;
100
+ /**
101
+ * The color to use to draw the symbol
102
+ * @default '#000000'
103
+ */
104
+ color?: string;
105
+ /**
106
+ * Whether to draw a halo (outline) around the string
107
+ * @default true;
108
+ */
109
+ halo?: boolean;
110
+ /**
111
+ * The color to use for the halo (outline)
112
+ * @default '#ffffff'
113
+ */
114
+ halo_color?: string;
23
115
  }
24
- interface StationPlotOpts {
116
+ /** Configuration for station plot sub-elements */
117
+ type SPConfig = SPNumberConfig | SPStringConfig | SPBarbConfig | SPSymbolConfig;
118
+ /**
119
+ * Configuration for station data plots
120
+ * @example
121
+ * spconfig : SPDataConfig<'id' | 'tmpf' | 'wind' | 'skyc'> = {
122
+ * // Add a string to the station plot (like the station ID)
123
+ * id: {type: 'string', pos: 'lr'},
124
+ *
125
+ * // Add a number to the station plot (like the temperature)
126
+ * tmpf: {type: 'number', pos: 'ul', color: '#cc0000', formatter: val => val === null ? '' : val.toFixed(0)},
127
+ *
128
+ * // Add a barb to the station plot
129
+ * wind: {type: 'barb', pos: 'c'},
130
+ *
131
+ * // Add a symbol to the station plot
132
+ * skyc: {type: 'symbol', pos: 'c'},
133
+ * }
134
+ */
135
+ type SPDataConfig<ObsFieldName extends string> = Record<ObsFieldName, SPConfig>;
136
+ /** Options for {@link StationPlot} components */
137
+ interface StationPlotOptions<ObsFieldName extends string> {
138
+ config: SPDataConfig<ObsFieldName>;
139
+ /**
140
+ * Thin factor at zoom level 1 for the station plots. Should be a power of 2.
141
+ * @default 1
142
+ */
143
+ thin_fac?: number;
144
+ /**
145
+ * Font face to use for plotting text
146
+ * @default 'Trebuchet MS'
147
+ */
148
+ font_face?: string;
149
+ /**
150
+ * Size of the font to use for the text
151
+ * @default 12
152
+ */
153
+ font_size?: number;
154
+ /**
155
+ * URL template to use in retrieving the font data for the text. The default is to use the template from the map style.
156
+ */
157
+ font_url_template?: string;
25
158
  }
26
- declare class StationPlot<ObsFieldName extends string, MapType extends MapLikeType> extends PlotComponent<MapType> {
27
- readonly field: ObsField<ObsFieldName>;
28
- readonly data_positions: SPDataPosition<ObsFieldName>;
29
- readonly opts: Required<StationPlotOpts>;
30
- constructor(field: ObsField<ObsFieldName>, data_positions: SPDataPosition<ObsFieldName>, opts: StationPlotOpts);
159
+ /**
160
+ * Station model plots for observed data
161
+ * @example
162
+ * // Specify how to set up the station plot
163
+ * const station_plot_locs = {
164
+ * tmpf: {type: 'number', pos: 'ul', color: '#cc0000', formatter: val => val === null ? '' : val.toFixed(0)},
165
+ * dwpf: {type: 'number', pos: 'll', color: '#00aa00', formatter: val => val === null ? '' : val.toFixed(0)},
166
+ * wind: {type: 'barb', pos: 'c'},
167
+ * preswx: {type: 'symbol', pos: 'cl', color: '#ff00ff'},
168
+ * skyc: {type: 'symbol', pos: 'c'},
169
+ * };
170
+ *
171
+ * // Create the station plot
172
+ * const station_plot = new StationPlot(obs_field, {config: station_plot_locs, thin_fac: 8, font_size: 14});
173
+ */
174
+ declare class StationPlot<GridType extends Grid, MapType extends MapLikeType, ObsFieldName extends string> extends PlotComponent<MapType> {
175
+ private field;
176
+ readonly opts: Required<StationPlotOptions<ObsFieldName>>;
177
+ private gl_elems;
178
+ private text_components;
179
+ /**
180
+ * Create station plots
181
+ * @param field - A field containing the observed data
182
+ * @param opts - Various options for the station plots
183
+ */
184
+ constructor(field: RawObsField<GridType, ObsFieldName>, opts: StationPlotOptions<ObsFieldName>);
185
+ /**
186
+ * Update the data displayed as station plots
187
+ * @param field - The new field to display as station plots
188
+ */
189
+ updateField(field: RawObsField<GridType, ObsFieldName>): Promise<void>;
190
+ /** @internal */
31
191
  onAdd(map: MapType, gl: WebGLAnyRenderingContext): Promise<void>;
32
- render(gl: WebGLAnyRenderingContext, matrix: Float32Array | number[]): void;
192
+ /** @internal */
193
+ render(gl: WebGLAnyRenderingContext, arg: RenderMethodArg): void;
33
194
  }
34
- export { StationPlot };
195
+ export default StationPlot;
196
+ export type { StationPlotOptions, SPPosition, SPNumberConfig, SPStringConfig, SPBarbConfig, SPSymbolConfig, SPConfig, SPDataConfig, SPSymbol };