autumnplot-gl 3.1.0 → 4.0.0-beta
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.
- package/README.md +6 -11
- package/dist/110.autumnplot-gl.js +1 -1
- package/dist/110.autumnplot-gl.js.map +1 -1
- package/dist/autumnplot-gl.js +1 -1
- package/dist/autumnplot-gl.js.map +1 -1
- package/dist/marchingsquares.wasm +0 -0
- package/lib/AutumnTypes.d.ts +53 -5
- package/lib/AutumnTypes.js +25 -1
- package/lib/Barbs.d.ts +23 -6
- package/lib/Barbs.js +20 -18
- package/lib/BillboardCollection.d.ts +16 -8
- package/lib/BillboardCollection.js +107 -59
- package/lib/Color.d.ts +57 -0
- package/lib/Color.js +163 -0
- package/lib/ColorBar.d.ts +12 -1
- package/lib/ColorBar.js +9 -7
- package/lib/Colormap.d.ts +19 -18
- package/lib/Colormap.js +84 -23
- package/lib/Contour.d.ts +42 -11
- package/lib/Contour.js +67 -58
- package/lib/ContourCreator.d.ts +4 -0
- package/lib/ContourCreator.js +2 -1
- package/lib/Fill.d.ts +27 -16
- package/lib/Fill.js +105 -83
- package/lib/Grid.d.ts +125 -29
- package/lib/Grid.js +303 -95
- package/lib/Hodographs.d.ts +24 -6
- package/lib/Hodographs.js +28 -24
- package/lib/Map.js +1 -1
- package/lib/Paintball.d.ts +6 -5
- package/lib/Paintball.js +38 -32
- package/lib/ParticleTracer.d.ts +19 -0
- package/lib/ParticleTracer.js +37 -0
- package/lib/PlotComponent.d.ts +6 -7
- package/lib/PlotComponent.js +17 -7
- package/lib/PlotLayer.d.ts +4 -4
- package/lib/PlotLayer.worker.d.ts +1 -2
- package/lib/PlotLayer.worker.js +22 -57
- package/lib/PolylineCollection.d.ts +18 -9
- package/lib/PolylineCollection.js +124 -89
- package/lib/RawField.d.ts +76 -23
- package/lib/RawField.js +138 -29
- package/lib/ShaderManager.d.ts +12 -0
- package/lib/ShaderManager.js +58 -0
- package/lib/StationPlot.d.ts +145 -0
- package/lib/StationPlot.js +205 -0
- package/lib/TextCollection.d.ts +12 -8
- package/lib/TextCollection.js +113 -71
- package/lib/cpp/marchingsquares.js +483 -585
- package/lib/cpp/marchingsquares.wasm +0 -0
- package/lib/cpp/marchingsquares_embind.d.ts +23 -3
- package/lib/index.d.ts +7 -4
- package/lib/index.js +5 -3
- package/lib/utils.d.ts +5 -8
- package/lib/utils.js +12 -83
- package/package.json +2 -2
package/lib/PlotLayer.worker.js
CHANGED
|
@@ -1,17 +1,11 @@
|
|
|
1
|
-
import { getMinZoom } from "./utils";
|
|
2
1
|
import * as Comlink from 'comlink';
|
|
3
2
|
import { LngLat } from "./Map";
|
|
4
|
-
function makeBBElements(field_lats, field_lons, field_ni, field_nj,
|
|
5
|
-
const
|
|
6
|
-
const n_coords_per_pt_pts = 3;
|
|
3
|
+
function makeBBElements(field_lats, field_lons, min_zoom, field_ni, field_nj, map_max_zoom) {
|
|
4
|
+
const n_coords_per_pt_pts = 2;
|
|
7
5
|
const n_coords_per_pt_tc = 2;
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const field_ni_access = Math.floor((field_ni - 1) / trim_inaccessible) + 1;
|
|
12
|
-
const field_nj_access = Math.floor((field_nj - 1) / trim_inaccessible) + 1;
|
|
13
|
-
const n_elems_pts = field_ni_access * field_nj_access * n_pts_per_poly * n_coords_per_pt_pts;
|
|
14
|
-
const n_elems_tc = field_ni_access * field_nj_access * n_pts_per_poly * n_coords_per_pt_tc;
|
|
6
|
+
const field_n_access = min_zoom.filter(mz => mz <= map_max_zoom).length;
|
|
7
|
+
const n_elems_pts = field_n_access * n_coords_per_pt_pts;
|
|
8
|
+
const n_elems_tc = field_n_access * n_coords_per_pt_tc;
|
|
15
9
|
let pts = new Float32Array(n_elems_pts);
|
|
16
10
|
let tex_coords = new Float32Array(n_elems_tc);
|
|
17
11
|
let istart_pts = 0;
|
|
@@ -21,22 +15,16 @@ function makeBBElements(field_lats, field_lons, field_ni, field_nj, thin_fac_bas
|
|
|
21
15
|
const idx = ilat * field_ni + ilon;
|
|
22
16
|
const lat = field_lats[idx];
|
|
23
17
|
const lon = field_lons[idx];
|
|
24
|
-
const zoom =
|
|
25
|
-
if (zoom >
|
|
26
|
-
continue;
|
|
18
|
+
const zoom = min_zoom[idx];
|
|
19
|
+
if (zoom > map_max_zoom || lon === undefined || lat === undefined)
|
|
20
|
+
continue; // TAS: Adding the checks for lat/lon here may be a bug waiting to happen? Not sure.
|
|
27
21
|
const pt_ll = new LngLat(lon, lat).toMercatorCoord();
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
pts[istart_pts + icrnr * n_coords_per_pt_pts + 2] = zoom * 4 + actual_icrnr;
|
|
35
|
-
tex_coords[istart_tc + icrnr * n_coords_per_pt_tc + 0] = ilon / (field_ni - 1);
|
|
36
|
-
tex_coords[istart_tc + icrnr * n_coords_per_pt_tc + 1] = ilat / (field_nj - 1);
|
|
37
|
-
}
|
|
38
|
-
istart_pts += (n_pts_per_poly * n_coords_per_pt_pts);
|
|
39
|
-
istart_tc += (n_pts_per_poly * n_coords_per_pt_tc);
|
|
22
|
+
pts[istart_pts + 0] = pt_ll.x;
|
|
23
|
+
pts[istart_pts + 1] = pt_ll.y;
|
|
24
|
+
tex_coords[istart_tc + 0] = ilon / (field_ni - 1) + zoom; // Pack the min zoom in with the texture coordinates; only works because the min zoom is always an integer
|
|
25
|
+
tex_coords[istart_tc + 1] = ilat / (field_nj - 1);
|
|
26
|
+
istart_pts += n_coords_per_pt_pts;
|
|
27
|
+
istart_tc += n_coords_per_pt_tc;
|
|
40
28
|
}
|
|
41
29
|
}
|
|
42
30
|
return { 'pts': pts, 'tex_coords': tex_coords };
|
|
@@ -44,7 +32,6 @@ function makeBBElements(field_lats, field_lons, field_ni, field_nj, thin_fac_bas
|
|
|
44
32
|
function makeDomainVerticesAndTexCoords(field_lats, field_lons, field_ni, field_nj, texcoord_margin_r, texcoord_margin_s) {
|
|
45
33
|
const verts = new Float32Array(2 * 2 * (field_ni - 1) * (field_nj + 1)).fill(0);
|
|
46
34
|
const tex_coords = new Float32Array(2 * 2 * (field_ni - 1) * (field_nj + 1)).fill(0);
|
|
47
|
-
const grid_cell_size = new Float32Array(1 * 2 * (field_ni - 1) * (field_nj + 1)).fill(0);
|
|
48
35
|
let ivert = 0;
|
|
49
36
|
let itexcoord = 0;
|
|
50
37
|
for (let i = 0; i < field_ni - 1; i++) {
|
|
@@ -83,29 +70,7 @@ function makeDomainVerticesAndTexCoords(field_lats, field_lons, field_ni, field_
|
|
|
83
70
|
}
|
|
84
71
|
}
|
|
85
72
|
}
|
|
86
|
-
|
|
87
|
-
for (let i = 0; i < field_ni - 1; i++) {
|
|
88
|
-
for (let j = 0; j < field_nj - 1; j++) {
|
|
89
|
-
const ivert = j == 0 ? 2 * (igcs + 1) : 2 * igcs;
|
|
90
|
-
const x_ll = verts[ivert], y_ll = verts[ivert + 1], x_lr = verts[ivert + 2], y_lr = verts[ivert + 3], x_ul = verts[ivert + 4], y_ul = verts[ivert + 5], x_ur = verts[ivert + 6], y_ur = verts[ivert + 7];
|
|
91
|
-
const area = 0.5 * Math.abs(x_ll * (y_lr - y_ul) + x_lr * (y_ul - y_ll) + x_ul * (y_ll - y_lr) +
|
|
92
|
-
x_ur * (y_ul - y_lr) + x_ul * (y_lr - y_ur) + x_lr * (y_ur - y_ul));
|
|
93
|
-
if (j == 0) {
|
|
94
|
-
grid_cell_size[igcs] = area;
|
|
95
|
-
igcs += 1;
|
|
96
|
-
}
|
|
97
|
-
grid_cell_size[igcs] = area;
|
|
98
|
-
grid_cell_size[igcs + 1] = area;
|
|
99
|
-
igcs += 2;
|
|
100
|
-
if (j == field_nj - 2) {
|
|
101
|
-
grid_cell_size[igcs] = area;
|
|
102
|
-
grid_cell_size[igcs + 1] = area;
|
|
103
|
-
grid_cell_size[igcs + 2] = area;
|
|
104
|
-
igcs += 3;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return { 'vertices': verts, 'tex_coords': tex_coords, 'grid_cell_size': grid_cell_size };
|
|
73
|
+
return { 'vertices': verts, 'tex_coords': tex_coords };
|
|
109
74
|
}
|
|
110
75
|
/*
|
|
111
76
|
function makePolylinesMiter(lines) {
|
|
@@ -288,10 +253,10 @@ function makePolylines(lines) {
|
|
|
288
253
|
return [v_ll.x, v_ll.y];
|
|
289
254
|
});
|
|
290
255
|
const has_offsets = line.offsets !== undefined;
|
|
291
|
-
const extrusion_verts =
|
|
256
|
+
const extrusion_verts = line.offsets !== undefined ? line.offsets : verts;
|
|
292
257
|
let pt_prev, pt_this = verts[0], pt_next = verts[1];
|
|
293
258
|
let ept_prev, ept_this = extrusion_verts[0], ept_next = extrusion_verts[1];
|
|
294
|
-
let len_prev, len_this = 0;
|
|
259
|
+
let len_prev, len_this = 0.0001;
|
|
295
260
|
let [ext_x, ext_y] = compute_normal_vec(ept_this, ept_next, !has_offsets);
|
|
296
261
|
ret.vertices[ilns.vertices++] = pt_this[0];
|
|
297
262
|
ret.vertices[ilns.vertices++] = pt_this[1];
|
|
@@ -308,13 +273,13 @@ function makePolylines(lines) {
|
|
|
308
273
|
len_this += Math.hypot(verts[ivt - 1][0] - verts[ivt][0], verts[ivt - 1][1] - verts[ivt][1]);
|
|
309
274
|
ret.vertices[ilns.vertices++] = pt_prev[0];
|
|
310
275
|
ret.vertices[ilns.vertices++] = pt_prev[1];
|
|
311
|
-
ret.vertices[ilns.vertices++] = len_prev;
|
|
276
|
+
ret.vertices[ilns.vertices++] = -len_prev;
|
|
312
277
|
ret.vertices[ilns.vertices++] = pt_prev[0];
|
|
313
278
|
ret.vertices[ilns.vertices++] = pt_prev[1];
|
|
314
279
|
ret.vertices[ilns.vertices++] = len_prev;
|
|
315
280
|
ret.vertices[ilns.vertices++] = pt_this[0];
|
|
316
281
|
ret.vertices[ilns.vertices++] = pt_this[1];
|
|
317
|
-
ret.vertices[ilns.vertices++] = len_this;
|
|
282
|
+
ret.vertices[ilns.vertices++] = -len_this;
|
|
318
283
|
ret.vertices[ilns.vertices++] = pt_this[0];
|
|
319
284
|
ret.vertices[ilns.vertices++] = pt_this[1];
|
|
320
285
|
ret.vertices[ilns.vertices++] = len_this;
|
|
@@ -332,7 +297,7 @@ function makePolylines(lines) {
|
|
|
332
297
|
ret.vertices[ilns.vertices++] = len_this;
|
|
333
298
|
ret.extrusion[ilns.extrusion++] = -ext_x;
|
|
334
299
|
ret.extrusion[ilns.extrusion++] = -ext_y;
|
|
335
|
-
if (
|
|
300
|
+
if (ret.offsets !== undefined && line.offsets !== undefined) {
|
|
336
301
|
const offsets = line.offsets;
|
|
337
302
|
let off_prev, off_this = offsets[0];
|
|
338
303
|
ret.offsets[ilns.offsets++] = off_this[0];
|
|
@@ -352,7 +317,7 @@ function makePolylines(lines) {
|
|
|
352
317
|
ret.offsets[ilns.offsets++] = off_this[0];
|
|
353
318
|
ret.offsets[ilns.offsets++] = off_this[1];
|
|
354
319
|
}
|
|
355
|
-
if (
|
|
320
|
+
if (ret.data !== undefined && line.data !== undefined) {
|
|
356
321
|
const data = line.data;
|
|
357
322
|
let data_prev, data_this = data[0];
|
|
358
323
|
ret.data[ilns.data++] = data_this;
|
|
@@ -366,7 +331,7 @@ function makePolylines(lines) {
|
|
|
366
331
|
}
|
|
367
332
|
ret.data[ilns.data++] = data_this;
|
|
368
333
|
}
|
|
369
|
-
if (
|
|
334
|
+
if (ret.zoom !== undefined && line.zoom !== undefined) {
|
|
370
335
|
for (let ivt = 0; ivt < verts.length * 4 - 2; ivt++) {
|
|
371
336
|
ret.zoom[ilns.zoom++] = line['zoom'];
|
|
372
337
|
}
|
|
@@ -1,28 +1,37 @@
|
|
|
1
|
-
import { LineData, WebGLAnyRenderingContext } from "./AutumnTypes";
|
|
1
|
+
import { LineData, WebGLAnyRenderingContext, RenderMethodArg } from "./AutumnTypes";
|
|
2
2
|
import { ColorMap } from "./Colormap";
|
|
3
|
+
/**
|
|
4
|
+
* A style to use to draw lines. The possible options are '-' for a solid line, '--' for a dashed line, ':' for a
|
|
5
|
+
* dotted line, '-.' for a dash-dot line, or you could pass a list of numbers (e.g., [1, 1, 1, 0, 1, 0]) to
|
|
6
|
+
* specify a custom dash scheme.
|
|
7
|
+
*/
|
|
8
|
+
type LineStyle = "-" | "--" | ":" | "-." | number[];
|
|
9
|
+
declare function isLineStyle(obj: any): obj is LineStyle;
|
|
3
10
|
interface PolylineCollectionOpts {
|
|
4
11
|
offset_scale?: number;
|
|
12
|
+
offset_rotates_with_map?: boolean;
|
|
5
13
|
color?: string;
|
|
6
14
|
cmap?: ColorMap;
|
|
7
15
|
line_width?: number;
|
|
16
|
+
line_style?: LineStyle | number[];
|
|
8
17
|
}
|
|
9
18
|
declare class PolylineCollection {
|
|
10
19
|
readonly width: number;
|
|
11
20
|
readonly scale: number | null;
|
|
12
|
-
private readonly
|
|
21
|
+
private readonly shader_manager;
|
|
13
22
|
private readonly vertices;
|
|
14
23
|
private readonly extrusion;
|
|
15
24
|
private readonly offset;
|
|
16
25
|
private readonly min_zoom;
|
|
17
26
|
private readonly line_data;
|
|
18
|
-
private readonly line_texture;
|
|
19
27
|
private readonly color;
|
|
20
|
-
private readonly
|
|
21
|
-
private readonly
|
|
22
|
-
private readonly
|
|
23
|
-
private readonly
|
|
28
|
+
private readonly cmap_gpu;
|
|
29
|
+
private readonly offset_rotates_with_map;
|
|
30
|
+
private readonly dash_texture;
|
|
31
|
+
private readonly n_dash;
|
|
24
32
|
private constructor();
|
|
25
33
|
static make(gl: WebGLAnyRenderingContext, lines: LineData[], opts?: PolylineCollectionOpts): Promise<PolylineCollection>;
|
|
26
|
-
render(gl: WebGLAnyRenderingContext,
|
|
34
|
+
render(gl: WebGLAnyRenderingContext, arg: RenderMethodArg, [map_width, map_height]: [number, number], map_zoom: number, map_bearing: number, map_pitch: number): void;
|
|
27
35
|
}
|
|
28
|
-
export { PolylineCollection };
|
|
36
|
+
export { PolylineCollection, isLineStyle };
|
|
37
|
+
export type { PolylineCollectionOpts, LineStyle };
|
|
@@ -1,40 +1,46 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
1
|
+
import { WGLBuffer, WGLTexture } from "autumn-wgl";
|
|
2
|
+
import { isWebGL2Ctx, getRendererData } from "./AutumnTypes";
|
|
3
|
+
import { ColorMap, ColorMapGPUInterface } from "./Colormap";
|
|
4
|
+
import { Color } from "./Color";
|
|
5
|
+
import { layer_worker } from "./PlotComponent";
|
|
6
|
+
import { ShaderProgramManager } from "./ShaderManager";
|
|
7
|
+
const polyline_vertex_src = `#version 300 es
|
|
8
|
+
|
|
7
9
|
uniform int u_offset;
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
in vec3 a_pos;
|
|
12
|
+
in vec2 a_extrusion;
|
|
13
|
+
in float a_data;
|
|
12
14
|
|
|
13
15
|
#ifdef ZOOM
|
|
14
|
-
|
|
16
|
+
in float a_min_zoom;
|
|
15
17
|
#endif
|
|
16
18
|
|
|
17
19
|
#ifdef OFFSET
|
|
18
|
-
|
|
20
|
+
in vec2 a_offset;
|
|
19
21
|
#endif
|
|
20
22
|
|
|
21
23
|
uniform lowp float u_line_width;
|
|
22
24
|
uniform lowp float u_map_width;
|
|
23
25
|
uniform lowp float u_map_height;
|
|
24
|
-
|
|
26
|
+
|
|
25
27
|
|
|
26
28
|
#ifdef ZOOM
|
|
27
29
|
uniform lowp float u_zoom;
|
|
28
30
|
#endif
|
|
29
31
|
|
|
30
32
|
#ifdef OFFSET
|
|
33
|
+
uniform int u_offset_rotates_with_map;
|
|
31
34
|
uniform lowp float u_offset_scale;
|
|
32
35
|
#endif
|
|
33
36
|
|
|
34
37
|
#ifdef DATA
|
|
35
|
-
|
|
38
|
+
out highp float v_data;
|
|
36
39
|
#endif
|
|
37
40
|
|
|
41
|
+
out highp float v_dist;
|
|
42
|
+
out lowp float v_cross;
|
|
43
|
+
|
|
38
44
|
mat4 scalingMatrix(float x_scale, float y_scale, float z_scale) {
|
|
39
45
|
return mat4(x_scale, 0.0, 0.0, 0.0,
|
|
40
46
|
0.0, y_scale, 0.0, 0.0,
|
|
@@ -66,23 +72,38 @@ void main() {
|
|
|
66
72
|
float globe_width = 1.;
|
|
67
73
|
vec2 globe_offset = vec2(globe_width * float(u_offset), 0.);
|
|
68
74
|
|
|
69
|
-
|
|
75
|
+
v_dist = abs(a_pos.z);
|
|
76
|
+
v_cross = sign(a_pos.z);
|
|
77
|
+
vec4 center_pos = projectTile(a_pos.xy + globe_offset);
|
|
70
78
|
vec4 offset = vec4(0.0, 0.0, 0.0, 0.0);
|
|
71
79
|
|
|
72
80
|
#ifdef ZOOM
|
|
73
81
|
if (u_zoom >= a_min_zoom) {
|
|
74
82
|
#endif
|
|
75
83
|
|
|
76
|
-
vec2 offset_ext = u_line_width *
|
|
84
|
+
vec2 offset_ext = u_line_width * 2. * a_extrusion;
|
|
77
85
|
|
|
78
86
|
mat4 map_stretch_matrix = scalingMatrix(u_map_height / u_map_width, 1., 1.);
|
|
79
|
-
|
|
87
|
+
|
|
88
|
+
vec4 center_pos_ihat = projectTile(a_pos.xy + globe_offset + vec2(1e-5, 0.));
|
|
89
|
+
|
|
90
|
+
vec2 center_east = normalize((inverse(map_stretch_matrix) * (center_pos_ihat - center_pos)).xy);
|
|
91
|
+
float globe_rotation = atan(center_east.x, center_east.y) - 3.141592654 / 2.0;
|
|
92
|
+
mat4 rotation_matrix = rotationZMatrix(-globe_rotation);
|
|
93
|
+
|
|
80
94
|
offset = map_stretch_matrix * rotation_matrix * vec4(offset_ext, 0., 0.);
|
|
81
95
|
|
|
82
96
|
#ifdef OFFSET
|
|
83
|
-
|
|
97
|
+
mat4 offset_matrix = scalingMatrix(1., u_map_width / u_map_height, 1.);
|
|
98
|
+
if (u_offset_rotates_with_map == 1) {
|
|
99
|
+
offset_matrix = offset_matrix * rotation_matrix;
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
offset = map_stretch_matrix * vec4(offset_ext, 0., 0.);
|
|
103
|
+
}
|
|
104
|
+
|
|
84
105
|
vec2 offset_offset = u_offset_scale * a_offset;
|
|
85
|
-
offset +=
|
|
106
|
+
offset += offset_matrix * vec4(offset_offset, 0., 0.);
|
|
86
107
|
#endif
|
|
87
108
|
|
|
88
109
|
#ifdef ZOOM
|
|
@@ -95,41 +116,72 @@ void main() {
|
|
|
95
116
|
v_data = a_data;
|
|
96
117
|
#endif
|
|
97
118
|
}`
|
|
98
|
-
const polyline_fragment_src = `#
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
uniform highp float u_cmap_min;
|
|
104
|
-
uniform highp float u_cmap_max;
|
|
105
|
-
uniform int u_n_index;
|
|
106
|
-
#else
|
|
119
|
+
const polyline_fragment_src = `#version 300 es
|
|
120
|
+
|
|
121
|
+
uniform sampler2D u_dash_sampler;
|
|
122
|
+
|
|
123
|
+
#ifndef DATA
|
|
107
124
|
uniform lowp vec4 u_color;
|
|
108
125
|
#endif
|
|
109
126
|
|
|
110
|
-
|
|
111
|
-
|
|
127
|
+
uniform lowp float u_line_width;
|
|
128
|
+
uniform lowp float u_zoom;
|
|
129
|
+
uniform int u_dash_pattern_length;
|
|
130
|
+
|
|
112
131
|
#ifdef DATA
|
|
113
|
-
|
|
114
|
-
|
|
132
|
+
in highp float v_data;
|
|
133
|
+
#endif
|
|
115
134
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
135
|
+
in highp float v_dist;
|
|
136
|
+
in lowp float v_cross;
|
|
137
|
+
|
|
138
|
+
out highp vec4 fragColor;
|
|
119
139
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
140
|
+
void main() {
|
|
141
|
+
lowp float dash_x = fract(v_dist * 2e2 * pow(2., floor(u_zoom)) / float(u_dash_pattern_length));
|
|
142
|
+
lowp float dash = texture(u_dash_sampler, vec2(dash_x, 0.5)).r;
|
|
143
|
+
|
|
144
|
+
lowp vec4 color;
|
|
145
|
+
#ifdef DATA
|
|
146
|
+
color = apply_colormap(v_data);
|
|
123
147
|
#else
|
|
124
148
|
color = u_color;
|
|
125
149
|
#endif
|
|
126
|
-
|
|
150
|
+
|
|
151
|
+
lowp float feather = clamp((1. - abs(v_cross)) * u_line_width, 0., 1.);
|
|
152
|
+
color.a *= (dash >= 1. ? 1. : 0.) * feather;
|
|
153
|
+
|
|
154
|
+
fragColor = color;
|
|
127
155
|
}`
|
|
156
|
+
const dash_arrays = {
|
|
157
|
+
"-": [1],
|
|
158
|
+
"--": [1, 1, 1, 1, 0, 0],
|
|
159
|
+
":": [1, 0],
|
|
160
|
+
"-.": [1, 1, 1, 0, 1, 0],
|
|
161
|
+
};
|
|
162
|
+
function isLineStyle(obj) {
|
|
163
|
+
return obj in dash_arrays || Array.isArray(obj) && obj.length > 0 && obj.map(e => typeof e === 'number').reduce((a, b) => a && b, true);
|
|
164
|
+
}
|
|
165
|
+
function makeDashTexture(gl, line_style) {
|
|
166
|
+
const dash_array = Array.isArray(line_style) ? line_style : dash_arrays[line_style];
|
|
167
|
+
const is_webgl2 = isWebGL2Ctx(gl);
|
|
168
|
+
const format = is_webgl2 ? gl.R8 : gl.LUMINANCE;
|
|
169
|
+
const type = gl.UNSIGNED_BYTE;
|
|
170
|
+
const row_alignment = 1;
|
|
171
|
+
const fill_image = { 'format': format, 'type': type,
|
|
172
|
+
'width': dash_array.length, 'height': 1, 'image': new Uint8Array(dash_array.map(d => d > 0 ? 255 : 0)),
|
|
173
|
+
'mag_filter': gl.NEAREST, 'row_alignment': row_alignment,
|
|
174
|
+
};
|
|
175
|
+
return [dash_array.length, new WGLTexture(gl, fill_image)];
|
|
176
|
+
}
|
|
128
177
|
class PolylineCollection {
|
|
129
178
|
constructor(gl, polyline, opts) {
|
|
130
179
|
opts = opts === undefined ? {} : opts;
|
|
131
|
-
|
|
180
|
+
const color_hex = opts.color === undefined ? '#000000' : opts.color;
|
|
181
|
+
this.color = Color.fromHex(color_hex);
|
|
132
182
|
const line_width = opts.line_width === undefined ? 1 : opts.line_width;
|
|
183
|
+
const line_style = opts.line_style === undefined ? '-' : opts.line_style;
|
|
184
|
+
this.offset_rotates_with_map = opts.offset_rotates_with_map === undefined ? true : opts.offset_rotates_with_map;
|
|
133
185
|
this.width = line_width;
|
|
134
186
|
const shader_defines = [];
|
|
135
187
|
this.vertices = new WGLBuffer(gl, polyline['vertices'], 3, gl.TRIANGLE_STRIP);
|
|
@@ -150,81 +202,64 @@ class PolylineCollection {
|
|
|
150
202
|
else {
|
|
151
203
|
this.min_zoom = null;
|
|
152
204
|
}
|
|
153
|
-
|
|
154
|
-
this.cmap_max = 3.40282347e+38;
|
|
205
|
+
let fragment_src = polyline_fragment_src;
|
|
155
206
|
if (polyline.data !== undefined) {
|
|
156
|
-
// TAS: this needs some cleanup (there's some repated code between here and the contour fills that should be combined?)
|
|
157
|
-
this.min_zoom = new WGLBuffer(gl, polyline.zoom, 1, gl.TRIANGLE_STRIP);
|
|
158
207
|
shader_defines.push('DATA');
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}
|
|
165
|
-
else {
|
|
166
|
-
tex_image = { 'format': gl.RGBA, 'type': gl.UNSIGNED_BYTE, 'image': makeTextureImage(opts.cmap), 'mag_filter': gl.NEAREST };
|
|
167
|
-
this.cmap_min = opts.cmap.levels[0];
|
|
168
|
-
this.cmap_max = opts.cmap.levels[opts.cmap.levels.length - 1];
|
|
169
|
-
this.index_map = makeIndexMap(opts.cmap);
|
|
170
|
-
}
|
|
171
|
-
const cmap_nonlin_image = { 'format': format_nonlin, 'type': type_nonlin,
|
|
172
|
-
'width': this.index_map.length, 'height': 1,
|
|
173
|
-
'image': new Uint16Array(this.index_map.buffer),
|
|
174
|
-
'mag_filter': gl.LINEAR, 'row_alignment': row_alignment_nonlin,
|
|
175
|
-
};
|
|
176
|
-
this.cmap_nonlin_texture = new WGLTexture(gl, cmap_nonlin_image);
|
|
177
|
-
this.line_texture = new WGLTexture(gl, tex_image);
|
|
178
|
-
this.line_data = new WGLBuffer(gl, polyline['data'], 1, gl.TRIANGLE_STRIP);
|
|
208
|
+
this.line_data = new WGLBuffer(gl, polyline.data, 1, gl.TRIANGLE_STRIP);
|
|
209
|
+
const cmap = opts.cmap === undefined ? new ColorMap([0, 1], [color_hex], { overflow_color: color_hex, underflow_color: color_hex }) : opts.cmap;
|
|
210
|
+
this.cmap_gpu = new ColorMapGPUInterface(cmap);
|
|
211
|
+
this.cmap_gpu.setupShaderVariables(gl, gl.NEAREST);
|
|
212
|
+
fragment_src = ColorMapGPUInterface.applyShader(fragment_src);
|
|
179
213
|
}
|
|
180
214
|
else {
|
|
181
|
-
this.line_texture = null;
|
|
182
215
|
this.line_data = null;
|
|
183
|
-
this.
|
|
216
|
+
this.cmap_gpu = null;
|
|
184
217
|
}
|
|
185
|
-
this.
|
|
218
|
+
[this.n_dash, this.dash_texture] = makeDashTexture(gl, line_style);
|
|
219
|
+
this.shader_manager = new ShaderProgramManager(polyline_vertex_src, fragment_src, shader_defines);
|
|
186
220
|
}
|
|
187
221
|
static async make(gl, lines, opts) {
|
|
188
222
|
const polylines = await layer_worker.makePolyLines(lines);
|
|
189
223
|
return new PolylineCollection(gl, polylines, opts);
|
|
190
224
|
}
|
|
191
|
-
render(gl,
|
|
192
|
-
|
|
193
|
-
|
|
225
|
+
render(gl, arg, [map_width, map_height], map_zoom, map_bearing, map_pitch) {
|
|
226
|
+
const render_data = getRendererData(arg);
|
|
227
|
+
const program = this.shader_manager.getShaderProgram(gl, render_data.shaderData);
|
|
194
228
|
const attributes = { 'a_pos': this.vertices, 'a_extrusion': this.extrusion };
|
|
195
229
|
const uniforms = {
|
|
196
|
-
'
|
|
230
|
+
'u_line_width': this.width, 'u_map_width': map_width, 'u_map_height': map_height, 'u_offset': 0, 'u_zoom': map_zoom,
|
|
231
|
+
'u_dash_pattern_length': this.n_dash, ...this.shader_manager.getShaderUniforms(render_data)
|
|
197
232
|
};
|
|
198
|
-
const textures = {};
|
|
199
|
-
if (this.offset !== null) {
|
|
233
|
+
const textures = { 'u_dash_sampler': this.dash_texture };
|
|
234
|
+
if (this.offset !== null && this.scale !== null) {
|
|
200
235
|
attributes['a_offset'] = this.offset;
|
|
201
236
|
uniforms['u_offset_scale'] = this.scale * (map_height / map_width);
|
|
237
|
+
uniforms['u_offset_rotates_with_map'] = this.offset_rotates_with_map ? 1 : 0;
|
|
202
238
|
}
|
|
203
239
|
if (this.min_zoom !== null) {
|
|
204
240
|
attributes['a_min_zoom'] = this.min_zoom;
|
|
205
|
-
uniforms['u_zoom'] = map_zoom;
|
|
206
241
|
}
|
|
207
|
-
if (this.line_data !== null
|
|
242
|
+
if (this.line_data !== null) {
|
|
208
243
|
attributes['a_data'] = this.line_data;
|
|
209
|
-
textures['u_cmap_sampler'] = this.line_texture;
|
|
210
|
-
textures['u_cmap_nonlin_sampler'] = this.cmap_nonlin_texture;
|
|
211
|
-
uniforms['u_cmap_min'] = this.cmap_min;
|
|
212
|
-
uniforms['u_cmap_max'] = this.cmap_max;
|
|
213
|
-
uniforms['u_n_index'] = this.index_map.length;
|
|
214
244
|
}
|
|
215
245
|
else {
|
|
216
|
-
uniforms['u_color'] = this.color;
|
|
246
|
+
uniforms['u_color'] = this.color.toRGBATuple();
|
|
247
|
+
}
|
|
248
|
+
program.use(attributes, uniforms, textures);
|
|
249
|
+
if (this.cmap_gpu !== null) {
|
|
250
|
+
this.cmap_gpu.bindShaderVariables(program);
|
|
217
251
|
}
|
|
218
|
-
this.program.use(attributes, uniforms, textures);
|
|
219
252
|
gl.enable(gl.BLEND);
|
|
220
253
|
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
254
|
+
program.draw();
|
|
255
|
+
if (render_data.type != 'maplibre' || !render_data.shaderData.define.includes('GLOBE')) {
|
|
256
|
+
program.setUniforms({ 'u_offset': -2 });
|
|
257
|
+
program.draw();
|
|
258
|
+
program.setUniforms({ 'u_offset': -1 });
|
|
259
|
+
program.draw();
|
|
260
|
+
program.setUniforms({ 'u_offset': 1 });
|
|
261
|
+
program.draw();
|
|
262
|
+
}
|
|
228
263
|
}
|
|
229
264
|
}
|
|
230
|
-
export { PolylineCollection };
|
|
265
|
+
export { PolylineCollection, isLineStyle };
|
package/lib/RawField.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Float16Array } from "@petamoriken/float16";
|
|
2
|
-
import { ContourData, TypedArray, WindProfile } from "./AutumnTypes";
|
|
2
|
+
import { ContourData, TypedArray, WebGLAnyRenderingContext, WindProfile } from "./AutumnTypes";
|
|
3
3
|
import { FieldContourOpts } from "./ContourCreator";
|
|
4
4
|
import { Grid } from "./Grid";
|
|
5
|
-
|
|
5
|
+
import { WGLTextureSpec } from "autumn-wgl";
|
|
6
|
+
type TextureDataType<ArrayType> = ArrayType extends Float32Array ? Float32Array : (ArrayType extends Uint8Array ? Uint8Array : Uint16Array);
|
|
6
7
|
/** A class representing a raw 2D field of gridded data, such as height or u wind. */
|
|
7
|
-
declare class RawScalarField<ArrayType extends TypedArray> {
|
|
8
|
-
readonly grid:
|
|
8
|
+
declare class RawScalarField<ArrayType extends TypedArray, GridType extends Grid> {
|
|
9
|
+
readonly grid: GridType;
|
|
9
10
|
readonly data: ArrayType;
|
|
10
11
|
private readonly contour_cache;
|
|
11
12
|
/**
|
|
@@ -13,9 +14,15 @@ declare class RawScalarField<ArrayType extends TypedArray> {
|
|
|
13
14
|
* @param grid - The grid on which the data are defined
|
|
14
15
|
* @param data - The data, which should be given as a 1D array in row-major order, with the first element being at the lower-left corner of the grid.
|
|
15
16
|
*/
|
|
16
|
-
constructor(grid:
|
|
17
|
+
constructor(grid: GridType, data: ArrayType);
|
|
17
18
|
/** @internal */
|
|
18
|
-
getTextureData
|
|
19
|
+
private getTextureData;
|
|
20
|
+
getWGLTextureSpec(gl: WebGLAnyRenderingContext, image_mag_filter: number): WGLTextureSpec;
|
|
21
|
+
/**
|
|
22
|
+
* Get contour data as an object with each contour level being a separate property.
|
|
23
|
+
* @param opts - Options for doing the contouring
|
|
24
|
+
* @returns contour data as an object
|
|
25
|
+
*/
|
|
19
26
|
getContours(opts: FieldContourOpts): Promise<ContourData>;
|
|
20
27
|
/**
|
|
21
28
|
* Create a new field by aggregating a number of fields using a specific function
|
|
@@ -26,7 +33,8 @@ declare class RawScalarField<ArrayType extends TypedArray> {
|
|
|
26
33
|
* // Compute wind speed from u and v
|
|
27
34
|
* wind_speed_field = RawScalarField.aggreateFields(Math.hypot, u_field, v_field);
|
|
28
35
|
*/
|
|
29
|
-
static aggregateFields<ArrayType extends TypedArray>(func: (...args: number[]) => number, ...args: RawScalarField<ArrayType>[]): RawScalarField<ArrayType>;
|
|
36
|
+
static aggregateFields<ArrayType extends TypedArray, GridType extends Grid>(func: (...args: number[]) => number, ...args: RawScalarField<ArrayType, GridType>[]): RawScalarField<ArrayType, GridType>;
|
|
37
|
+
sampleField(lon: number, lat: number): number;
|
|
30
38
|
}
|
|
31
39
|
type VectorRelativeTo = 'earth' | 'grid';
|
|
32
40
|
interface RawVectorFieldOptions {
|
|
@@ -37,9 +45,9 @@ interface RawVectorFieldOptions {
|
|
|
37
45
|
relative_to?: VectorRelativeTo;
|
|
38
46
|
}
|
|
39
47
|
/** A class representing a 2D gridded field of vectors */
|
|
40
|
-
declare class RawVectorField<ArrayType extends TypedArray> {
|
|
41
|
-
readonly u: RawScalarField<ArrayType>;
|
|
42
|
-
readonly v: RawScalarField<ArrayType>;
|
|
48
|
+
declare class RawVectorField<ArrayType extends TypedArray, GridType extends Grid> {
|
|
49
|
+
readonly u: RawScalarField<ArrayType, GridType>;
|
|
50
|
+
readonly v: RawScalarField<ArrayType, GridType>;
|
|
43
51
|
readonly relative_to: VectorRelativeTo;
|
|
44
52
|
/**
|
|
45
53
|
* Create a vector field.
|
|
@@ -48,26 +56,71 @@ declare class RawVectorField<ArrayType extends TypedArray> {
|
|
|
48
56
|
* @param v - The v (north/south) component of the vectors, which should be given as a 1D array in row-major order, with the first element being at the lower-left corner of the grid
|
|
49
57
|
* @param opts - Options for creating the vector field.
|
|
50
58
|
*/
|
|
51
|
-
constructor(grid:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
59
|
+
constructor(grid: GridType, u: ArrayType, v: ArrayType, opts?: RawVectorFieldOptions);
|
|
60
|
+
/** @internal */
|
|
61
|
+
private getTextureData;
|
|
62
|
+
getWGLTextureSpecs(gl: WebGLAnyRenderingContext, mag_filter: number): {
|
|
63
|
+
u: WGLTextureSpec;
|
|
64
|
+
v: WGLTextureSpec;
|
|
55
65
|
};
|
|
56
|
-
|
|
57
|
-
|
|
66
|
+
/** @internal */
|
|
67
|
+
getThinnedField(thin_fac: number, map_max_zoom: number): RawVectorField<ArrayType, Grid>;
|
|
68
|
+
/** @internal */
|
|
69
|
+
get grid(): GridType;
|
|
70
|
+
sampleField(lon: number, lat: number): [number, number];
|
|
58
71
|
}
|
|
59
72
|
/** A class grid of wind profiles */
|
|
60
|
-
declare class RawProfileField {
|
|
73
|
+
declare class RawProfileField<GridType extends Grid> {
|
|
61
74
|
readonly profiles: WindProfile[];
|
|
62
|
-
readonly grid:
|
|
75
|
+
readonly grid: GridType;
|
|
63
76
|
/**
|
|
64
77
|
* Create a grid of wind profiles
|
|
65
78
|
* @param grid - The grid on which the profiles are defined
|
|
66
79
|
* @param profiles - The wind profiles themselves, which should be given as a 1D array in row-major order, with the first profile being at the lower-left corner of the grid
|
|
67
80
|
*/
|
|
68
|
-
constructor(grid:
|
|
69
|
-
/**
|
|
70
|
-
|
|
81
|
+
constructor(grid: GridType, profiles: WindProfile[]);
|
|
82
|
+
/**
|
|
83
|
+
* @internal
|
|
84
|
+
* Get the gridded storm motion vector field
|
|
85
|
+
*/
|
|
86
|
+
getStormMotionGrid(): RawVectorField<Float16Array, GridType>;
|
|
87
|
+
/** @internal */
|
|
88
|
+
getProfileCoords(): {
|
|
89
|
+
lats: Float32Array;
|
|
90
|
+
lons: Float32Array;
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Type for an observation data point
|
|
95
|
+
* @example
|
|
96
|
+
* const obs : ObsRawData<'t' | 'td'> = {'t': 71, 'td': 66};
|
|
97
|
+
*/
|
|
98
|
+
type ObsRawData<ObsFieldName extends string> = Record<ObsFieldName, string | number | [number, number] | null>;
|
|
99
|
+
/** Raw observation data, given as a list of objects */
|
|
100
|
+
declare class RawObsField<GridType extends Grid, ObsFieldName extends string> {
|
|
101
|
+
readonly grid: GridType;
|
|
102
|
+
readonly data: ObsRawData<ObsFieldName>[];
|
|
103
|
+
/**
|
|
104
|
+
* Create a field of observations
|
|
105
|
+
* @param grid - The grid on which the obs are defined (can be either a structured or unstructured grid)
|
|
106
|
+
* @param data - The observation data. Conceptually, obs are given as a list of individual observations.
|
|
107
|
+
*/
|
|
108
|
+
constructor(grid: GridType, data: ObsRawData<ObsFieldName>[]);
|
|
109
|
+
/**
|
|
110
|
+
* @internal
|
|
111
|
+
* Get observation element as a list of scalar numbers
|
|
112
|
+
*/
|
|
113
|
+
getScalar(key: ObsFieldName): (number | null)[];
|
|
114
|
+
/**
|
|
115
|
+
* @internal
|
|
116
|
+
* Get observed element as a list of strings (internal method)
|
|
117
|
+
*/
|
|
118
|
+
getStrings(key: ObsFieldName): (string | null)[];
|
|
119
|
+
/**
|
|
120
|
+
* @internal
|
|
121
|
+
* Get observed element as a list of vectors (internal method)
|
|
122
|
+
*/
|
|
123
|
+
getVector(key: ObsFieldName): RawVectorField<Float16Array, GridType>;
|
|
71
124
|
}
|
|
72
|
-
export { RawScalarField, RawVectorField, RawProfileField };
|
|
73
|
-
export type { RawVectorFieldOptions, VectorRelativeTo, TextureDataType };
|
|
125
|
+
export { RawScalarField, RawVectorField, RawProfileField, RawObsField };
|
|
126
|
+
export type { RawVectorFieldOptions, VectorRelativeTo, TextureDataType, ObsRawData };
|