autumnplot-gl 2.2.3 → 3.1.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 (49) hide show
  1. package/README.md +65 -8
  2. package/dist/110.autumnplot-gl.js +2 -0
  3. package/dist/110.autumnplot-gl.js.map +1 -0
  4. package/dist/autumnplot-gl.js +3 -0
  5. package/dist/autumnplot-gl.js.LICENSE.txt +12 -0
  6. package/dist/autumnplot-gl.js.map +1 -0
  7. package/dist/marchingsquares.wasm +0 -0
  8. package/lib/AutumnTypes.d.ts +14 -13
  9. package/lib/Barbs.d.ts +10 -5
  10. package/lib/Barbs.js +22 -5
  11. package/lib/BillboardCollection.d.ts +11 -7
  12. package/lib/BillboardCollection.js +52 -30
  13. package/lib/ColorBar.js +51 -7
  14. package/lib/Colormap.d.ts +14 -3
  15. package/lib/Colormap.js +63 -12
  16. package/lib/Contour.d.ts +73 -18
  17. package/lib/Contour.js +180 -148
  18. package/lib/ContourCreator.d.ts +18 -0
  19. package/lib/ContourCreator.js +23 -0
  20. package/lib/Fill.d.ts +18 -7
  21. package/lib/Fill.js +73 -41
  22. package/lib/Grid.d.ts +167 -0
  23. package/lib/Grid.js +339 -0
  24. package/lib/Hodographs.d.ts +13 -6
  25. package/lib/Hodographs.js +58 -71
  26. package/lib/Map.d.ts +14 -3
  27. package/lib/Map.js +9 -0
  28. package/lib/Paintball.d.ts +11 -5
  29. package/lib/Paintball.js +35 -15
  30. package/lib/PlotComponent.d.ts +5 -5
  31. package/lib/PlotLayer.d.ts +12 -12
  32. package/lib/PlotLayer.js +16 -14
  33. package/lib/PlotLayer.worker.d.ts +2 -2
  34. package/lib/PlotLayer.worker.js +105 -66
  35. package/lib/PolylineCollection.d.ts +20 -9
  36. package/lib/PolylineCollection.js +158 -32
  37. package/lib/RawField.d.ts +11 -167
  38. package/lib/RawField.js +37 -383
  39. package/lib/TextCollection.d.ts +31 -0
  40. package/lib/TextCollection.js +295 -0
  41. package/lib/cpp/marchingsquares.d.ts +6 -0
  42. package/lib/cpp/marchingsquares.js +3449 -0
  43. package/lib/cpp/marchingsquares.wasm +0 -0
  44. package/lib/cpp/marchingsquares_embind.d.ts +6 -0
  45. package/lib/index.d.ts +13 -6
  46. package/lib/index.js +12 -3
  47. package/lib/utils.d.ts +5 -3
  48. package/lib/utils.js +17 -6
  49. package/package.json +14 -9
package/lib/Contour.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { TypedArray, WebGLAnyRenderingContext } from './AutumnTypes';
2
- import { MapType } from './Map';
2
+ import { MapLikeType } from './Map';
3
3
  import { PlotComponent } from './PlotComponent';
4
4
  import { RawScalarField } from './RawField';
5
5
  interface ContourOptions {
@@ -18,34 +18,31 @@ interface ContourOptions {
18
18
  * @default Draw contours at regular intervals given by the `interval` option.
19
19
  */
20
20
  levels?: number[];
21
- /**
22
- * A function to thin the contours based on zoom level. The function should take a zoom level and return a number `n` that means to only show every
23
- * `n`th contour.
24
- * @default Don't thin the contours on any zoom level
25
- */
26
- thinner?: (zoom: number) => number;
27
21
  }
28
22
  /**
29
- * A field of contoured data. The contours can optionally be thinned based on map zoom level.
23
+ * A field of contoured data.
30
24
  * @example
31
25
  * // Create a contoured height field, with black contours every 30 m (assuming the height field is in
32
- * // meters) and only using every other contour when the map zoom level is less than 5.
33
- * const contours = new Contour(height_field, {color: '#000000', interval: 30,
34
- * thinner: zoom => zoom < 5 ? 2 : 1});
26
+ * // meters).
27
+ * const contours = new Contour(height_field, {color: '#000000', interval: 30});
35
28
  */
36
- declare class Contour<ArrayType extends TypedArray> extends PlotComponent {
37
- private readonly field;
38
- readonly color: [number, number, number];
39
- readonly interval: number;
40
- readonly levels: number[];
41
- readonly thinner: (zoom: number) => number;
29
+ declare class Contour<ArrayType extends TypedArray, MapType extends MapLikeType> extends PlotComponent<MapType> {
30
+ private field;
31
+ readonly opts: Required<ContourOptions>;
42
32
  private gl_elems;
33
+ private contours;
43
34
  /**
44
35
  * Create a contoured field
45
36
  * @param field - The field to contour
46
37
  * @param opts - Options for creating the contours
47
38
  */
48
39
  constructor(field: RawScalarField<ArrayType>, opts: ContourOptions);
40
+ /**
41
+ * Update the data displayed as contours
42
+ * @param field - The new field to contour
43
+ */
44
+ updateField(field: RawScalarField<ArrayType>): Promise<void>;
45
+ getContours(): Promise<import("./AutumnTypes").ContourData>;
49
46
  /**
50
47
  * @internal
51
48
  * Add the contours to a map
@@ -57,5 +54,63 @@ declare class Contour<ArrayType extends TypedArray> extends PlotComponent {
57
54
  */
58
55
  render(gl: WebGLAnyRenderingContext, matrix: number[] | Float32Array): void;
59
56
  }
57
+ interface ContourLabelOptions {
58
+ /**
59
+ * Number of decimal places to use in the contour labels
60
+ * @default 0
61
+ */
62
+ n_decimal_places?: number;
63
+ /**
64
+ * Font face to use for the contour labels
65
+ * @default 'Trebuchet MS'
66
+ */
67
+ font_face?: string;
68
+ /**
69
+ * Font size in points to use for the contour labels
70
+ * @default 12
71
+ */
72
+ font_size?: number;
73
+ /**
74
+ * URL template to use in retrieving the font data for the labels. The default is to use the template from the map style.
75
+ */
76
+ font_url_template?: string;
77
+ /**
78
+ * Text color for the contour labels
79
+ * @default '#000000'
80
+ */
81
+ text_color?: string;
82
+ /**
83
+ * Halo (outline) color for the contour labels
84
+ * @default '#000000'
85
+ */
86
+ halo_color?: string;
87
+ /**
88
+ * Whether to draw the halo (outline) on the contour labels
89
+ * @default false
90
+ */
91
+ halo?: boolean;
92
+ }
93
+ declare class ContourLabels<ArrayType extends TypedArray, MapType extends MapLikeType> extends PlotComponent<MapType> {
94
+ private readonly contours;
95
+ private gl_elems;
96
+ private text_collection;
97
+ private readonly opts;
98
+ constructor(contours: Contour<ArrayType, MapType>, opts?: ContourLabelOptions);
99
+ /**
100
+ * Update contour labels when the field for the associated Contour object has been changed.
101
+ */
102
+ updateField(): Promise<void>;
103
+ /**
104
+ * @internal
105
+ * Add the contour labels to a map
106
+ */
107
+ onAdd(map: MapType, gl: WebGLAnyRenderingContext): Promise<void>;
108
+ /**
109
+ * @internal
110
+ * Render the contour labels
111
+ */
112
+ render(gl: WebGLAnyRenderingContext, matrix: number[]): void;
113
+ }
60
114
  export default Contour;
61
- export type { ContourOptions };
115
+ export { ContourLabels };
116
+ export type { ContourOptions, ContourLabelOptions };
package/lib/Contour.js CHANGED
@@ -1,116 +1,20 @@
1
- import { PlotComponent, getGLFormatTypeAlignment } from './PlotComponent';
2
- import { hex2rgba } from './utils';
3
- import { WGLProgram, WGLTexture } from 'autumn-wgl';
4
- const contour_vertex_shader_src = `uniform mat4 u_matrix;
5
- uniform int u_offset;
6
-
7
- attribute vec2 a_pos;
8
- attribute float a_grid_cell_size;
9
- attribute vec2 a_tex_coord;
10
-
11
- varying highp vec2 v_tex_coord;
12
- varying highp float v_grid_cell_size;
13
- varying highp float v_map_scale_fac;
14
-
15
- void main() {
16
- float globe_width = 1.;
17
- vec2 globe_offset = vec2(globe_width * float(u_offset), 0.);
18
-
19
- gl_Position = u_matrix * vec4(a_pos + globe_offset, 0.0, 1.0);
20
- v_tex_coord = a_tex_coord;
21
- v_grid_cell_size = a_grid_cell_size;
22
-
23
-
24
- highp float lat = 2. * atan(exp(a_pos.y / 6371229.0)) - 3.1414592654 / 2.;
25
- v_map_scale_fac = cos(lat);
26
- }`
27
- const contour_fragment_shader_src = `#extension GL_OES_standard_derivatives : enable
28
- #define MAX_N_CONTOURS 40
29
-
30
- varying highp vec2 v_tex_coord;
31
- varying highp float v_grid_cell_size;
32
- varying highp float v_map_scale_fac;
33
-
34
- uniform sampler2D u_fill_sampler;
35
- uniform highp float u_contour_interval;
36
- uniform highp float u_contour_levels[MAX_N_CONTOURS];
37
- uniform int u_num_contours;
38
- uniform lowp float u_line_cutoff;
39
- uniform lowp vec3 u_color;
40
- uniform lowp vec2 u_step_size;
41
- uniform lowp float u_zoom_fac;
42
-
43
- void main() {
44
- highp float field_val = texture2D(u_fill_sampler, v_tex_coord).r;
45
-
46
- lowp vec2 grid_point = fract(v_tex_coord / u_step_size + vec2(0.5, 0.5));
47
- highp vec2 grid_sw = v_tex_coord - grid_point * u_step_size;
48
-
49
- lowp vec2 ihat = vec2(u_step_size.x, 0.0);
50
- lowp vec2 jhat = vec2(0.0, u_step_size.y);
51
- highp float fv_sw = texture2D(u_fill_sampler, grid_sw).r;
52
- highp float fv_se = texture2D(u_fill_sampler, grid_sw + ihat).r;
53
- highp float fv_nw = texture2D(u_fill_sampler, grid_sw + jhat).r;
54
- highp float fv_ne = texture2D(u_fill_sampler, grid_sw + ihat + jhat).r;
55
-
56
- highp float dfdx = mix(fv_se, fv_ne, grid_point.y) - mix(fv_sw, fv_nw, grid_point.y);
57
- highp float dfdy = mix(fv_nw, fv_ne, grid_point.x) - mix(fv_sw, fv_se, grid_point.x);
58
- highp float fwidth_field = sqrt((dfdx * dfdx + dfdy * dfdy) / (5e5 * v_grid_cell_size));
59
-
60
- lowp float plot_val;
61
-
62
- if (u_num_contours > 0) {
63
- highp float min_contour_diff = u_contour_levels[1] - u_contour_levels[0];
64
- bool assigned_contour = false;
65
- highp float low_contour;
66
- highp float high_contour;
67
- highp float highest_contour;
68
-
69
- for (int ncnt = 0; ncnt < MAX_N_CONTOURS; ncnt++) {
70
- if (ncnt >= u_num_contours) { break; }
71
-
72
- min_contour_diff = min(min_contour_diff, u_contour_levels[ncnt + 1] - u_contour_levels[ncnt]);
73
-
74
- if (u_contour_levels[ncnt] < field_val && field_val < u_contour_levels[ncnt + 1]) {
75
- assigned_contour = true;
76
-
77
- low_contour = u_contour_levels[ncnt];
78
- high_contour = u_contour_levels[ncnt + 1];
79
- }
80
-
81
- highest_contour = u_contour_levels[ncnt];
82
- }
83
-
84
- if (!assigned_contour) {
85
- if (field_val < u_contour_levels[0]) {
86
- plot_val = (u_contour_levels[0] - field_val) / min_contour_diff;
87
- }
88
- else if (field_val > highest_contour) {
89
- plot_val = (field_val - highest_contour) / min_contour_diff;
90
- }
91
- }
92
- else {
93
- plot_val = min(field_val - low_contour, high_contour - field_val) / min_contour_diff;
94
- }
95
- }
96
- else {
97
- plot_val = fract(field_val / u_contour_interval);
98
- if (plot_val > 0.5) plot_val = 1.0 - plot_val;
99
- }
100
-
101
- plot_val = plot_val / (max(0.001, fwidth_field / (u_zoom_fac * 0.125)));
102
-
103
- if (plot_val > u_line_cutoff) discard;
104
-
105
- gl_FragColor = vec4(u_color, 1. - (plot_val * plot_val / (u_line_cutoff * u_line_cutoff)));
106
- }`
1
+ import { LngLat } from './Map';
2
+ import { PlotComponent } from './PlotComponent';
3
+ import { PolylineCollection } from './PolylineCollection';
4
+ import { TextCollection } from './TextCollection';
5
+ import { hex2rgb, normalizeOptions } from './utils';
6
+ import { kdTree } from 'kd-tree-javascript';
7
+ const contour_opt_defaults = {
8
+ color: '#000000',
9
+ interval: 1,
10
+ levels: undefined
11
+ };
107
12
  /**
108
- * A field of contoured data. The contours can optionally be thinned based on map zoom level.
13
+ * A field of contoured data.
109
14
  * @example
110
15
  * // Create a contoured height field, with black contours every 30 m (assuming the height field is in
111
- * // meters) and only using every other contour when the map zoom level is less than 5.
112
- * const contours = new Contour(height_field, {color: '#000000', interval: 30,
113
- * thinner: zoom => zoom < 5 ? 2 : 1});
16
+ * // meters).
17
+ * const contours = new Contour(height_field, {color: '#000000', interval: 30});
114
18
  */
115
19
  class Contour extends PlotComponent {
116
20
  /**
@@ -121,65 +25,193 @@ class Contour extends PlotComponent {
121
25
  constructor(field, opts) {
122
26
  super();
123
27
  this.field = field;
124
- this.interval = opts.interval || 1;
125
- this.levels = opts.levels || [];
126
- const color = hex2rgba(opts.color || '#000000');
127
- this.color = [color[0], color[1], color[2]];
128
- this.thinner = opts.thinner || (() => 1);
28
+ this.opts = normalizeOptions(opts, contour_opt_defaults);
129
29
  this.gl_elems = null;
30
+ this.contours = null;
31
+ }
32
+ /**
33
+ * Update the data displayed as contours
34
+ * @param field - The new field to contour
35
+ */
36
+ async updateField(field) {
37
+ this.field = field;
38
+ if (this.gl_elems === null)
39
+ return;
40
+ const gl = this.gl_elems.gl;
41
+ const contour_data = await this.getContours();
42
+ const line_data = Object.values(contour_data).flat().map(c => {
43
+ return { vertices: c };
44
+ });
45
+ this.contours = await PolylineCollection.make(gl, line_data, { line_width: 2, color: this.opts.color });
46
+ this.gl_elems.map.triggerRepaint();
47
+ }
48
+ async getContours() {
49
+ return await this.field.getContours({ interval: this.opts.interval, levels: this.opts.levels });
130
50
  }
131
51
  /**
132
52
  * @internal
133
53
  * Add the contours to a map
134
54
  */
135
55
  async onAdd(map, gl) {
136
- // Basic procedure for these contours from https://www.shadertoy.com/view/lltBWM
137
- gl.getExtension("OES_standard_derivatives");
138
- const program = new WGLProgram(gl, contour_vertex_shader_src, contour_fragment_shader_src);
139
- const { vertices: verts_buf, texcoords: tex_coords_buf, cellsize: cellsize_buf } = await this.field.grid.getWGLBuffers(gl);
140
- const vertices = verts_buf;
141
- const texcoords = tex_coords_buf;
142
- const grid_cell_size = cellsize_buf;
143
- const { format, type, row_alignment } = getGLFormatTypeAlignment(gl, this.field.isFloat16());
144
- const fill_image = { 'format': format, 'type': type,
145
- 'width': this.field.grid.ni, 'height': this.field.grid.nj, 'image': this.field.getTextureData(),
146
- 'mag_filter': gl.LINEAR, 'row_alignment': row_alignment,
147
- };
148
- const fill_texture = new WGLTexture(gl, fill_image);
149
56
  this.gl_elems = {
150
- map: map, program: program, vertices: vertices, texcoords: texcoords, grid_cell_size: grid_cell_size, fill_texture: fill_texture
57
+ gl: gl, map: map
151
58
  };
59
+ await this.updateField(this.field);
152
60
  }
153
61
  /**
154
62
  * @internal
155
63
  * Render the contours
156
64
  */
157
65
  render(gl, matrix) {
158
- if (this.gl_elems === null)
66
+ if (this.gl_elems === null || this.contours === null)
159
67
  return;
160
68
  const gl_elems = this.gl_elems;
161
69
  if (matrix instanceof Float32Array)
162
70
  matrix = [...matrix];
163
71
  const zoom = gl_elems.map.getZoom();
164
- const intv = this.thinner(zoom) * this.interval;
165
- const cutoff = 0.3 / intv;
166
- const step_size = [1 / this.field.grid.ni, 1 / this.field.grid.nj];
167
- const zoom_fac = Math.pow(2, zoom);
168
- let uniforms = { 'u_contour_interval': intv, 'u_line_cutoff': cutoff, 'u_color': this.color, 'u_step_size': step_size, 'u_zoom_fac': zoom_fac,
169
- 'u_matrix': matrix, 'u_num_contours': 0, 'u_contour_levels': [0], 'u_offset': 0 };
170
- if (this.levels.length > 0) {
171
- uniforms = { ...uniforms, 'u_num_contours': this.levels.length, 'u_contour_levels': this.levels };
72
+ const map_width = gl_elems.map.getCanvas().width;
73
+ const map_height = gl_elems.map.getCanvas().height;
74
+ const bearing = gl_elems.map.getBearing();
75
+ const pitch = gl_elems.map.getPitch();
76
+ this.contours.render(gl, matrix, [map_width, map_height], zoom, bearing, pitch);
77
+ }
78
+ }
79
+ const contour_label_opt_defaults = {
80
+ n_decimal_places: 0,
81
+ font_face: 'Trebuchet MS',
82
+ font_size: 12,
83
+ font_url_template: '',
84
+ text_color: '#000000',
85
+ halo_color: '#000000',
86
+ halo: false
87
+ };
88
+ class ContourLabels extends PlotComponent {
89
+ constructor(contours, opts) {
90
+ super();
91
+ this.opts = normalizeOptions(opts, contour_label_opt_defaults);
92
+ this.contours = contours;
93
+ this.text_collection = null;
94
+ this.gl_elems = null;
95
+ }
96
+ /**
97
+ * Update contour labels when the field for the associated Contour object has been changed.
98
+ */
99
+ async updateField() {
100
+ if (this.gl_elems === null)
101
+ return;
102
+ const map = this.gl_elems.map;
103
+ const gl = this.gl_elems.gl;
104
+ const map_style = map.getStyle();
105
+ const font_url_template = this.opts.font_url_template == '' ? map_style.glyphs : this.opts.font_url_template;
106
+ const font_url = font_url_template.replace('{range}', '0-255').replace('{fontstack}', this.opts.font_face);
107
+ const label_pos = [];
108
+ const contour_data = await this.contours.getContours();
109
+ const contour_levels = Object.keys(contour_data).map(parseFloat);
110
+ contour_levels.sort((a, b) => a - b);
111
+ const map_max_zoom = map.getMaxZoom();
112
+ const contour_label_spacing = 0.01 * Math.pow(2, 7 - map_max_zoom);
113
+ let min_label_lat = null, max_label_lat = null, min_label_lon = null, max_label_lon = null;
114
+ Object.entries(contour_data).forEach(([level, contours]) => {
115
+ const icntr = (parseFloat(level) - contour_levels[0]);
116
+ const level_str = level.toString();
117
+ contours.forEach(contour => {
118
+ const c_map = contour.map(v => {
119
+ const v_ll = new LngLat(...v).toMercatorCoord();
120
+ return [v_ll.x, v_ll.y];
121
+ });
122
+ const dist = [];
123
+ c_map.forEach((v, i) => {
124
+ if (i == 0) {
125
+ dist.push(0);
126
+ }
127
+ else {
128
+ const v_last = c_map[i - 1];
129
+ const this_dist = Math.hypot(v_last[0] - v[0], v_last[1] - v[1]);
130
+ dist.push(dist[i - 1] + this_dist);
131
+ }
132
+ });
133
+ let n_labels_placed = 0;
134
+ for (let idist = 1; idist < dist.length; idist++) {
135
+ const target_dist = contour_label_spacing * (n_labels_placed + (icntr / 2) % 1);
136
+ if (dist[idist - 1] <= target_dist && target_dist < dist[idist]) {
137
+ const pt1 = contour[idist - 1];
138
+ const pt2 = contour[idist];
139
+ const alpha = (target_dist - dist[idist - 1]) / (dist[idist] - dist[idist - 1]);
140
+ const pt_lon = (1 - alpha) * pt1[0] + alpha * pt2[0];
141
+ const pt_lat = (1 - alpha) * pt1[1] + alpha * pt2[1];
142
+ if (min_label_lon === null || pt_lon < min_label_lon)
143
+ min_label_lon = pt_lon;
144
+ if (max_label_lon === null || pt_lon > max_label_lon)
145
+ max_label_lon = pt_lon;
146
+ if (min_label_lat === null || pt_lat < min_label_lat)
147
+ min_label_lat = pt_lat;
148
+ if (max_label_lat === null || pt_lat > max_label_lat)
149
+ max_label_lat = pt_lat;
150
+ label_pos.push({ lon: pt_lon, lat: pt_lat, min_zoom: map_max_zoom, text: level_str });
151
+ n_labels_placed++;
152
+ }
153
+ }
154
+ });
155
+ });
156
+ const tree = new kdTree(label_pos, (a, b) => Math.hypot(a.lon - b.lon, a.lat - b.lat), ['lon', 'lat']);
157
+ const { x: min_label_x, y: max_label_y } = new LngLat(min_label_lon, min_label_lat).toMercatorCoord();
158
+ const { x: max_label_x, y: min_label_y } = new LngLat(max_label_lon, max_label_lat).toMercatorCoord();
159
+ const thin_grid_width = max_label_x - min_label_x;
160
+ const thin_grid_height = max_label_y - min_label_y;
161
+ const ni_thin_grid = Math.round(4 * thin_grid_width / contour_label_spacing);
162
+ const nj_thin_grid = Math.round(4 * thin_grid_height / contour_label_spacing);
163
+ const thin_grid_xs = [];
164
+ const thin_grid_ys = [];
165
+ for (let idx = 0; idx < ni_thin_grid; idx++) {
166
+ thin_grid_xs.push(min_label_x + (idx / ni_thin_grid) * thin_grid_width);
167
+ }
168
+ for (let jdy = 0; jdy < nj_thin_grid; jdy++) {
169
+ thin_grid_ys.push(min_label_y + (jdy / nj_thin_grid) * thin_grid_height);
172
170
  }
173
- gl_elems.program.use({ 'a_pos': gl_elems.vertices, 'a_grid_cell_size': gl_elems.grid_cell_size, 'a_tex_coord': gl_elems.texcoords }, uniforms, { 'u_fill_sampler': gl_elems.fill_texture });
174
- gl.enable(gl.BLEND);
175
- gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
176
- gl_elems.program.draw();
177
- gl_elems.program.setUniforms({ 'u_offset': -2 });
178
- gl_elems.program.draw();
179
- gl_elems.program.setUniforms({ 'u_offset': -1 });
180
- gl_elems.program.draw();
181
- gl_elems.program.setUniforms({ 'u_offset': 1 });
182
- gl_elems.program.draw();
171
+ let skip = 1;
172
+ for (let zoom = map_max_zoom - 1; zoom >= 0; zoom--) {
173
+ for (let idx = 0; idx < ni_thin_grid; idx += skip) {
174
+ for (let jdy = 0; jdy < nj_thin_grid; jdy += skip) {
175
+ const grid_x = thin_grid_xs[idx];
176
+ const grid_y = thin_grid_ys[jdy];
177
+ const ll = LngLat.fromMercatorCoord(grid_x, grid_y);
178
+ const [label, dist] = tree.nearest({ lon: ll.lng, lat: ll.lat, min_zoom: 0, text: "" }, 1)[0];
179
+ label.min_zoom = zoom;
180
+ }
181
+ }
182
+ skip *= 2;
183
+ }
184
+ const tc_opts = {
185
+ horizontal_align: 'center', vertical_align: 'middle', font_size: this.opts.font_size,
186
+ halo: this.opts.halo,
187
+ text_color: hex2rgb(this.opts.text_color), halo_color: hex2rgb(this.opts.halo_color),
188
+ };
189
+ this.text_collection = await TextCollection.make(gl, label_pos, font_url, tc_opts);
190
+ map.triggerRepaint();
191
+ }
192
+ /**
193
+ * @internal
194
+ * Add the contour labels to a map
195
+ */
196
+ async onAdd(map, gl) {
197
+ this.gl_elems = {
198
+ gl: gl, map: map,
199
+ };
200
+ this.updateField();
201
+ }
202
+ /**
203
+ * @internal
204
+ * Render the contour labels
205
+ */
206
+ render(gl, matrix) {
207
+ if (this.gl_elems === null || this.text_collection === null)
208
+ return;
209
+ const gl_elems = this.gl_elems;
210
+ const map_width = gl_elems.map.getCanvas().width;
211
+ const map_height = gl_elems.map.getCanvas().height;
212
+ const map_zoom = gl_elems.map.getZoom();
213
+ this.text_collection.render(gl, matrix, [map_width, map_height], map_zoom);
183
214
  }
184
215
  }
185
216
  export default Contour;
217
+ export { ContourLabels };
@@ -0,0 +1,18 @@
1
+ import { MarchingSquaresModule } from './cpp/marchingsquares';
2
+ import './cpp/marchingsquares.wasm';
3
+ import { Grid } from "./Grid";
4
+ import { ContourData, TypedArray } from "./AutumnTypes";
5
+ declare function initMSModule(): Promise<MarchingSquaresModule>;
6
+ interface FieldContourOpts {
7
+ /**
8
+ * The interval at which to create contours. The field will be contoured at this interval from its minimum to its maximum.
9
+ */
10
+ interval?: number;
11
+ /**
12
+ * Contour the field at these specific levels.
13
+ */
14
+ levels?: number[];
15
+ }
16
+ declare function contourCreator<ArrayType extends TypedArray>(data: ArrayType, grid: Grid, opts: FieldContourOpts): Promise<ContourData>;
17
+ export { contourCreator, initMSModule };
18
+ export type { FieldContourOpts };
@@ -0,0 +1,23 @@
1
+ import Module from './cpp/marchingsquares';
2
+ import './cpp/marchingsquares.wasm';
3
+ let msm_promise = null;
4
+ function initMSModule() {
5
+ if (msm_promise === null) {
6
+ msm_promise = Module();
7
+ }
8
+ return msm_promise;
9
+ }
10
+ async function contourCreator(data, grid, opts) {
11
+ if (opts.interval === undefined && opts.levels === undefined) {
12
+ throw "Must supply either an interval or levels to contourCreator()";
13
+ }
14
+ const interval = opts.interval === undefined ? 0 : opts.interval;
15
+ const msm = await initMSModule();
16
+ const grid_coords = grid.getGridCoords();
17
+ const getContourLevels = data instanceof Float32Array ? msm.getContourLevelsFloat32 : msm.getContourLevelsFloat16;
18
+ const makeContours = data instanceof Float32Array ? msm.makeContoursFloat32 : msm.makeContoursFloat16;
19
+ 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
+ return contours;
22
+ }
23
+ export { contourCreator, initMSModule };
package/lib/Fill.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { PlotComponent } from './PlotComponent';
2
2
  import { ColorMap } from './Colormap';
3
3
  import { RawScalarField } from './RawField';
4
- import { MapType } from './Map';
4
+ import { MapLikeType } from './Map';
5
5
  import { TypedArray, WebGLAnyRenderingContext } from './AutumnTypes';
6
6
  interface ContourFillOptions {
7
7
  /** The color map to use when creating the fills */
@@ -21,16 +21,17 @@ interface RasterOptions {
21
21
  */
22
22
  opacity?: number;
23
23
  }
24
- declare class PlotComponentFill<ArrayType extends TypedArray> extends PlotComponent {
25
- private readonly field;
26
- readonly cmap: ColorMap;
27
- readonly opacity: number;
24
+ declare class PlotComponentFill<ArrayType extends TypedArray, MapType extends MapLikeType> extends PlotComponent<MapType> {
25
+ private field;
26
+ readonly opts: Required<ContourFillOptions>;
28
27
  private readonly cmap_image;
29
28
  private readonly index_map;
30
29
  private gl_elems;
30
+ private fill_texture;
31
31
  protected image_mag_filter: number | null;
32
32
  protected cmap_mag_filter: number | null;
33
33
  constructor(field: RawScalarField<ArrayType>, opts: ContourFillOptions);
34
+ updateField(field: RawScalarField<ArrayType>): Promise<void>;
34
35
  onAdd(map: MapType, gl: WebGLAnyRenderingContext): Promise<void>;
35
36
  render(gl: WebGLAnyRenderingContext, matrix: number[] | Float32Array): void;
36
37
  }
@@ -40,13 +41,18 @@ declare class PlotComponentFill<ArrayType extends TypedArray> extends PlotCompon
40
41
  * // Create a raster plot with the provided color map
41
42
  * const raster = new Raster(wind_speed_field, {cmap: color_map});
42
43
  */
43
- declare class Raster<ArrayType extends TypedArray> extends PlotComponentFill<ArrayType> {
44
+ declare class Raster<ArrayType extends TypedArray, MapType extends MapLikeType> extends PlotComponentFill<ArrayType, MapType> {
44
45
  /**
45
46
  * Create a raster plot
46
47
  * @param field - The field to create the raster plot from
47
48
  * @param opts - Options for creating the raster plot
48
49
  */
49
50
  constructor(field: RawScalarField<ArrayType>, opts: RasterOptions);
51
+ /**
52
+ * Update the data displayed as a raster plot
53
+ * @param field - The new field to display as a raster plot
54
+ */
55
+ updateField(field: RawScalarField<ArrayType>): Promise<void>;
50
56
  /**
51
57
  * @internal
52
58
  * Add the raster plot to a map
@@ -64,13 +70,18 @@ declare class Raster<ArrayType extends TypedArray> extends PlotComponentFill<Arr
64
70
  * // Create a field of filled contours with the provided color map
65
71
  * const fill = new ContourFill(wind_speed_field, {cmap: color_map});
66
72
  */
67
- declare class ContourFill<ArrayType extends TypedArray> extends PlotComponentFill<ArrayType> {
73
+ declare class ContourFill<ArrayType extends TypedArray, MapType extends MapLikeType> extends PlotComponentFill<ArrayType, MapType> {
68
74
  /**
69
75
  * Create a filled contoured field
70
76
  * @param field - The field to create filled contours from
71
77
  * @param opts - Options for creating the filled contours
72
78
  */
73
79
  constructor(field: RawScalarField<ArrayType>, opts: ContourFillOptions);
80
+ /**
81
+ * Update the data displayed as filled contours
82
+ * @param field - The new field to display as filled contours
83
+ */
84
+ updateField(field: RawScalarField<ArrayType>): Promise<void>;
74
85
  /**
75
86
  * @internal
76
87
  * Add the filled contours to a map