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.
- package/README.md +11 -207
- package/dist/983.autumnplot-gl.js +2 -0
- package/dist/983.autumnplot-gl.js.map +1 -0
- 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 +85 -6
- package/lib/AutumnTypes.js +28 -1
- package/lib/Barbs.d.ts +7 -5
- package/lib/BillboardCollection.d.ts +8 -7
- package/lib/BillboardCollection.js +69 -58
- package/lib/Color.d.ts +2 -0
- package/lib/Color.js +4 -0
- package/lib/ColorBar.d.ts +19 -0
- package/lib/ColorBar.js +9 -6
- package/lib/Colormap.d.ts +1 -0
- package/lib/Colormap.js +8 -8
- package/lib/Contour.d.ts +28 -8
- package/lib/Contour.js +27 -54
- package/lib/ContourCreator.d.ts +9 -1
- package/lib/ContourCreator.js +5 -4
- package/lib/Fill.d.ts +28 -14
- package/lib/Fill.js +97 -50
- package/lib/Grid.d.ts +132 -30
- package/lib/Grid.js +355 -99
- package/lib/Hodographs.d.ts +15 -8
- package/lib/Hodographs.js +48 -30
- package/lib/Map.d.ts +14 -1
- package/lib/Map.js +60 -4
- package/lib/Paintball.d.ts +7 -5
- package/lib/Paintball.js +36 -31
- package/lib/ParticleTracer.d.ts +19 -0
- package/lib/ParticleTracer.js +37 -0
- package/lib/PlotComponent.d.ts +7 -7
- package/lib/PlotComponent.js +9 -3
- package/lib/PlotLayer.d.ts +5 -5
- package/lib/PlotLayer.js +2 -2
- package/lib/PlotLayer.worker.d.ts +1 -2
- package/lib/PlotLayer.worker.js +22 -51
- package/lib/PolylineCollection.d.ts +5 -3
- package/lib/PolylineCollection.js +60 -37
- package/lib/RawField.d.ts +78 -23
- package/lib/RawField.js +147 -31
- package/lib/ShaderManager.d.ts +12 -0
- package/lib/ShaderManager.js +58 -0
- package/lib/StationPlot.d.ts +187 -25
- package/lib/StationPlot.js +209 -60
- package/lib/TextCollection.d.ts +9 -6
- package/lib/TextCollection.js +97 -62
- 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 +12 -5
- package/lib/index.js +8 -5
- package/lib/utils.d.ts +4 -1
- package/lib/utils.js +12 -1
- package/package.json +3 -3
- package/dist/110.autumnplot-gl.js +0 -2
- package/dist/110.autumnplot-gl.js.map +0 -1
package/lib/Hodographs.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { PlotComponent } from "./PlotComponent";
|
|
2
2
|
import { MapLikeType } from "./Map";
|
|
3
3
|
import { RawProfileField } from "./RawField";
|
|
4
|
-
import { WebGLAnyRenderingContext } from "./AutumnTypes";
|
|
4
|
+
import { RenderMethodArg, WebGLAnyRenderingContext } from "./AutumnTypes";
|
|
5
5
|
import { ColorMap } from "./Colormap";
|
|
6
|
+
import { Grid } from "./Grid";
|
|
7
|
+
/** Options for {@link Hodographs} components */
|
|
6
8
|
interface HodographOptions {
|
|
7
9
|
/**
|
|
8
10
|
* The color of the hodograph plot background as a hex string
|
|
@@ -19,19 +21,24 @@ interface HodographOptions {
|
|
|
19
21
|
* The width of the hodograph line in pixels
|
|
20
22
|
* @default 2.5
|
|
21
23
|
*/
|
|
22
|
-
hodo_line_width
|
|
24
|
+
hodo_line_width?: number;
|
|
23
25
|
/**
|
|
24
26
|
* The width of the lines on the background in pixels
|
|
25
27
|
* @default 1.5
|
|
26
28
|
*/
|
|
27
|
-
background_line_width
|
|
29
|
+
background_line_width?: number;
|
|
28
30
|
/**
|
|
29
31
|
* The colormap to use for the heights on the hodograph. Default is a yellow-blue colormap.
|
|
30
32
|
*/
|
|
31
|
-
height_cmap
|
|
33
|
+
height_cmap?: ColorMap;
|
|
34
|
+
/**
|
|
35
|
+
* The wind speed (in kts) of the largest ring on the hodograph background
|
|
36
|
+
* @default 80
|
|
37
|
+
*/
|
|
38
|
+
max_wind_speed_ring?: number;
|
|
32
39
|
}
|
|
33
40
|
/** A class representing a field of hodograph plots */
|
|
34
|
-
declare class Hodographs<MapType extends MapLikeType> extends PlotComponent<MapType> {
|
|
41
|
+
declare class Hodographs<GridType extends Grid, MapType extends MapLikeType> extends PlotComponent<MapType> {
|
|
35
42
|
private profile_field;
|
|
36
43
|
readonly opts: Required<HodographOptions>;
|
|
37
44
|
private gl_elems;
|
|
@@ -44,12 +51,12 @@ declare class Hodographs<MapType extends MapLikeType> extends PlotComponent<MapT
|
|
|
44
51
|
* @param profile_field - The grid of profiles to plot
|
|
45
52
|
* @param opts - Various options to use when creating the hodographs
|
|
46
53
|
*/
|
|
47
|
-
constructor(profile_field: RawProfileField
|
|
54
|
+
constructor(profile_field: RawProfileField<GridType>, opts?: HodographOptions);
|
|
48
55
|
/**
|
|
49
56
|
* Update the profiles displayed
|
|
50
57
|
* @param field - The new profiles to display as hodographs
|
|
51
58
|
*/
|
|
52
|
-
updateField(field: RawProfileField): Promise<void>;
|
|
59
|
+
updateField(field: RawProfileField<GridType>): Promise<void>;
|
|
53
60
|
/**
|
|
54
61
|
* @internal
|
|
55
62
|
* Add the hodographs to a map
|
|
@@ -59,7 +66,7 @@ declare class Hodographs<MapType extends MapLikeType> extends PlotComponent<MapT
|
|
|
59
66
|
* @internal
|
|
60
67
|
* Render the hodographs
|
|
61
68
|
*/
|
|
62
|
-
render(gl: WebGLAnyRenderingContext,
|
|
69
|
+
render(gl: WebGLAnyRenderingContext, arg: RenderMethodArg): void;
|
|
63
70
|
}
|
|
64
71
|
export default Hodographs;
|
|
65
72
|
export type { HodographOptions };
|
package/lib/Hodographs.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { PlotComponent } from "./PlotComponent";
|
|
2
2
|
import { PolylineCollection } from "./PolylineCollection";
|
|
3
3
|
import { BillboardCollection } from "./BillboardCollection";
|
|
4
|
-
import {
|
|
4
|
+
import { normalizeOptions } from './utils';
|
|
5
|
+
import { isStormRelativeWindProfile } from "./AutumnTypes";
|
|
5
6
|
import { ColorMap } from "./Colormap";
|
|
6
7
|
import { Color } from "./Color";
|
|
7
8
|
const LINE_WIDTH_MULTIPLIER = 2.5;
|
|
8
|
-
const BG_MAX_RING_MAG = 40;
|
|
9
9
|
const HODO_BG_DIMS = {
|
|
10
10
|
BB_WIDTH: 256,
|
|
11
11
|
BB_HEIGHT: 256,
|
|
@@ -15,7 +15,7 @@ const HODO_BG_DIMS = {
|
|
|
15
15
|
BB_MAG_WRAP: 1000,
|
|
16
16
|
BB_MAG_BIN_SIZE: 1000,
|
|
17
17
|
};
|
|
18
|
-
function _createHodoBackgroundTexture(line_width) {
|
|
18
|
+
function _createHodoBackgroundTexture(line_width, arrow_head) {
|
|
19
19
|
let canvas = document.createElement('canvas');
|
|
20
20
|
canvas.width = HODO_BG_DIMS.BB_TEX_WIDTH;
|
|
21
21
|
canvas.height = HODO_BG_DIMS.BB_TEX_HEIGHT;
|
|
@@ -29,14 +29,21 @@ function _createHodoBackgroundTexture(line_width) {
|
|
|
29
29
|
ctx.arc(HODO_BG_DIMS.BB_TEX_WIDTH / 2, HODO_BG_DIMS.BB_TEX_WIDTH / 2, irng - line_width / 2, 0, 2 * Math.PI);
|
|
30
30
|
ctx.stroke();
|
|
31
31
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
32
|
+
if (arrow_head) {
|
|
33
|
+
const ctr_x = HODO_BG_DIMS.BB_TEX_WIDTH / 2, ctr_y = HODO_BG_DIMS.BB_TEX_WIDTH / 2;
|
|
34
|
+
const arrow_size = 20;
|
|
35
|
+
ctx.beginPath();
|
|
36
|
+
ctx.moveTo(ctr_x, ctr_y);
|
|
37
|
+
ctx.lineTo(ctr_x + arrow_size / 2, ctr_y + arrow_size);
|
|
38
|
+
ctx.lineTo(ctr_x - arrow_size / 2, ctr_y + arrow_size);
|
|
39
|
+
ctx.lineTo(ctr_x, ctr_y);
|
|
40
|
+
ctx.fill();
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
ctx.beginPath();
|
|
44
|
+
ctx.arc(HODO_BG_DIMS.BB_TEX_WIDTH / 2, HODO_BG_DIMS.BB_TEX_WIDTH / 2, line_width, 0, 2 * Math.PI);
|
|
45
|
+
ctx.fill();
|
|
46
|
+
}
|
|
40
47
|
return canvas;
|
|
41
48
|
}
|
|
42
49
|
;
|
|
@@ -46,7 +53,8 @@ const hodograph_opt_defaults = {
|
|
|
46
53
|
thin_fac: 1,
|
|
47
54
|
hodo_line_width: 2.5,
|
|
48
55
|
background_line_width: 1.5,
|
|
49
|
-
height_cmap: HODO_CMAP
|
|
56
|
+
height_cmap: HODO_CMAP,
|
|
57
|
+
max_wind_speed_ring: 80
|
|
50
58
|
};
|
|
51
59
|
/** A class representing a field of hodograph plots */
|
|
52
60
|
class Hodographs extends PlotComponent {
|
|
@@ -59,8 +67,8 @@ class Hodographs extends PlotComponent {
|
|
|
59
67
|
super();
|
|
60
68
|
this.profile_field = profile_field;
|
|
61
69
|
this.opts = normalizeOptions(opts, hodograph_opt_defaults);
|
|
62
|
-
this.hodo_bg_texture = _createHodoBackgroundTexture(this.opts.background_line_width * LINE_WIDTH_MULTIPLIER);
|
|
63
|
-
this.hodo_scale = (HODO_BG_DIMS.BB_TEX_WIDTH - this.opts.background_line_width / 2) / (HODO_BG_DIMS.BB_TEX_WIDTH *
|
|
70
|
+
this.hodo_bg_texture = _createHodoBackgroundTexture(this.opts.background_line_width * LINE_WIDTH_MULTIPLIER, isStormRelativeWindProfile(profile_field.profiles[0]));
|
|
71
|
+
this.hodo_scale = (HODO_BG_DIMS.BB_TEX_WIDTH - this.opts.background_line_width / 2) / (HODO_BG_DIMS.BB_TEX_WIDTH * this.opts.max_wind_speed_ring);
|
|
64
72
|
this.bg_size = 140;
|
|
65
73
|
this.gl_elems = null;
|
|
66
74
|
this.line_elems = null;
|
|
@@ -77,29 +85,39 @@ class Hodographs extends PlotComponent {
|
|
|
77
85
|
const gl = this.gl_elems.gl;
|
|
78
86
|
this.gl_elems.bg_billboard.updateField(field.getStormMotionGrid());
|
|
79
87
|
const profiles = this.profile_field.profiles;
|
|
80
|
-
const
|
|
81
|
-
|
|
88
|
+
const { lats, lons } = this.profile_field.getProfileCoords();
|
|
89
|
+
const min_visible_zoom = this.profile_field.grid.getMinVisibleZoom(this.opts.thin_fac);
|
|
90
|
+
const hodo_polyline = profiles.map((prof, iprof) => {
|
|
82
91
|
return {
|
|
83
|
-
'offsets': [...prof['u']].map((u, ipt) =>
|
|
84
|
-
|
|
85
|
-
|
|
92
|
+
'offsets': [...prof['u']].map((u, ipt) => {
|
|
93
|
+
if (isStormRelativeWindProfile(prof)) {
|
|
94
|
+
return [u - prof['smu'], prof['v'][ipt] - prof['smv']];
|
|
95
|
+
}
|
|
96
|
+
return [u, prof['v'][ipt]];
|
|
97
|
+
}),
|
|
98
|
+
'vertices': [...prof['u']].map(u => [lons[iprof], lats[iprof]]),
|
|
99
|
+
'zoom': min_visible_zoom[iprof],
|
|
86
100
|
'data': [...prof['z']],
|
|
87
101
|
};
|
|
88
102
|
});
|
|
89
|
-
const hodo_line = await PolylineCollection.make(gl, hodo_polyline, { line_width: this.opts.hodo_line_width, cmap: this.opts.height_cmap,
|
|
90
|
-
|
|
91
|
-
|
|
103
|
+
const hodo_line = await PolylineCollection.make(gl, hodo_polyline, { line_width: this.opts.hodo_line_width, cmap: this.opts.height_cmap,
|
|
104
|
+
offset_scale: this.hodo_scale * this.bg_size, offset_rotates_with_map: false });
|
|
105
|
+
const sm_polyline = profiles.map((prof, iprof) => {
|
|
106
|
+
if (!isStormRelativeWindProfile(prof)) {
|
|
107
|
+
return { vertices: [] };
|
|
108
|
+
}
|
|
92
109
|
const sm_mag = Math.hypot(prof['smu'], prof['smv']);
|
|
93
110
|
const sm_ang = Math.PI / 2 - Math.atan2(-prof['smv'], -prof['smu']);
|
|
94
111
|
const buffer = 2;
|
|
95
112
|
return {
|
|
96
113
|
'offsets': [[buffer * Math.sin(sm_ang), buffer * Math.cos(sm_ang)],
|
|
97
114
|
[sm_mag * Math.sin(sm_ang), sm_mag * Math.cos(sm_ang)]],
|
|
98
|
-
'vertices': [[
|
|
99
|
-
'zoom':
|
|
115
|
+
'vertices': [[lons[iprof], lats[iprof]], [lons[iprof], lats[iprof]]],
|
|
116
|
+
'zoom': min_visible_zoom[iprof]
|
|
100
117
|
};
|
|
101
118
|
});
|
|
102
|
-
const sm_line = await PolylineCollection.make(gl, sm_polyline, { line_width: this.opts.background_line_width, color: this.opts.bgcolor,
|
|
119
|
+
const sm_line = await PolylineCollection.make(gl, sm_polyline, { line_width: this.opts.background_line_width, color: this.opts.bgcolor,
|
|
120
|
+
offset_scale: this.hodo_scale * this.bg_size, offset_rotates_with_map: false });
|
|
103
121
|
this.line_elems = {
|
|
104
122
|
hodo_line: hodo_line, sm_line: sm_line
|
|
105
123
|
};
|
|
@@ -111,7 +129,7 @@ class Hodographs extends PlotComponent {
|
|
|
111
129
|
async onAdd(map, gl) {
|
|
112
130
|
const bg_image = { 'format': gl.RGBA, 'type': gl.UNSIGNED_BYTE, 'image': this.hodo_bg_texture, 'mag_filter': gl.NEAREST };
|
|
113
131
|
const max_zoom = map.getMaxZoom();
|
|
114
|
-
const bg_billboard = new BillboardCollection(this.profile_field.getStormMotionGrid(), this.opts.thin_fac, max_zoom, bg_image, HODO_BG_DIMS, this.bg_size * 0.004, { color: Color.fromHex(this.opts.bgcolor) });
|
|
132
|
+
const bg_billboard = new BillboardCollection(this.profile_field.getStormMotionGrid(), this.opts.thin_fac, max_zoom, bg_image, HODO_BG_DIMS, this.bg_size * 0.004, { color: Color.fromHex(this.opts.bgcolor), rotate_with_map: false });
|
|
115
133
|
await bg_billboard.setup(gl);
|
|
116
134
|
this.gl_elems = {
|
|
117
135
|
gl: gl, map: map, bg_billboard: bg_billboard
|
|
@@ -122,7 +140,7 @@ class Hodographs extends PlotComponent {
|
|
|
122
140
|
* @internal
|
|
123
141
|
* Render the hodographs
|
|
124
142
|
*/
|
|
125
|
-
render(gl,
|
|
143
|
+
render(gl, arg) {
|
|
126
144
|
if (this.gl_elems === null || this.line_elems === null)
|
|
127
145
|
return;
|
|
128
146
|
const gl_elems = this.gl_elems;
|
|
@@ -132,9 +150,9 @@ class Hodographs extends PlotComponent {
|
|
|
132
150
|
const map_height = gl_elems.map.getCanvas().height;
|
|
133
151
|
const bearing = gl_elems.map.getBearing();
|
|
134
152
|
const pitch = gl_elems.map.getPitch();
|
|
135
|
-
line_elems.hodo_line.render(gl,
|
|
136
|
-
line_elems.sm_line.render(gl,
|
|
137
|
-
gl_elems.bg_billboard.render(gl,
|
|
153
|
+
line_elems.hodo_line.render(gl, arg, [map_width, map_height], zoom, bearing, pitch);
|
|
154
|
+
line_elems.sm_line.render(gl, arg, [map_width, map_height], zoom, bearing, bearing);
|
|
155
|
+
gl_elems.bg_billboard.render(gl, arg, [map_width, map_height], zoom, bearing, pitch);
|
|
138
156
|
}
|
|
139
157
|
}
|
|
140
158
|
export default Hodographs;
|
package/lib/Map.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
type StyleSpecification = {
|
|
2
2
|
glyphs?: string;
|
|
3
3
|
};
|
|
4
|
+
/** Type with the required methods for mapping libraries */
|
|
4
5
|
type MapLikeType = {
|
|
5
6
|
triggerRepaint: () => void;
|
|
6
7
|
getCanvas: () => HTMLCanvasElement;
|
|
@@ -14,6 +15,8 @@ interface LambertConformalConicParameters {
|
|
|
14
15
|
lon_0: number;
|
|
15
16
|
lat_0: number;
|
|
16
17
|
lat_std: [number, number] | number;
|
|
18
|
+
a: number;
|
|
19
|
+
b: number;
|
|
17
20
|
}
|
|
18
21
|
declare function lambertConformalConic(params: LambertConformalConicParameters): (a: number, b: number, opts?: {
|
|
19
22
|
inverse: boolean;
|
|
@@ -26,6 +29,16 @@ interface RotateSphereParams {
|
|
|
26
29
|
declare function rotateSphere(params: RotateSphereParams): (a: number, b: number, opts?: {
|
|
27
30
|
inverse: boolean;
|
|
28
31
|
}) => [number, number];
|
|
32
|
+
interface VerticalPerspectiveParams {
|
|
33
|
+
lat_0: number;
|
|
34
|
+
lon_0: number;
|
|
35
|
+
alt: number;
|
|
36
|
+
a: number;
|
|
37
|
+
b: number;
|
|
38
|
+
}
|
|
39
|
+
declare function verticalPerspective(params: VerticalPerspectiveParams): (a: number, b: number, opts?: {
|
|
40
|
+
inverse: boolean;
|
|
41
|
+
}) => [number, number];
|
|
29
42
|
/**
|
|
30
43
|
* A `LngLat` object represents a given longitude and latitude coordinate, measured in degrees.
|
|
31
44
|
* These coordinates are based on the [WGS84 (EPSG:4326) standard](https://en.wikipedia.org/wiki/World_Geodetic_System#WGS84).
|
|
@@ -49,5 +62,5 @@ declare class LngLat {
|
|
|
49
62
|
};
|
|
50
63
|
static fromMercatorCoord(x: number, y: number): LngLat;
|
|
51
64
|
}
|
|
52
|
-
export { LngLat, lambertConformalConic, rotateSphere };
|
|
65
|
+
export { LngLat, lambertConformalConic, rotateSphere, verticalPerspective };
|
|
53
66
|
export type { MapLikeType };
|
package/lib/Map.js
CHANGED
|
@@ -9,8 +9,8 @@ function lambertConformalConic(params) {
|
|
|
9
9
|
return Math.cos(lat) / Math.sqrt(1 - eccen * eccen * sin_lat * sin_lat);
|
|
10
10
|
};
|
|
11
11
|
// WGS 84 spheroid
|
|
12
|
-
const semimajor =
|
|
13
|
-
const semiminor =
|
|
12
|
+
const semimajor = params.a;
|
|
13
|
+
const semiminor = params.b;
|
|
14
14
|
const eccen = Math.sqrt(1 - (semiminor * semiminor) / (semimajor * semimajor));
|
|
15
15
|
const radians = Math.PI / 180;
|
|
16
16
|
let { lon_0, lat_0, lat_std } = params;
|
|
@@ -121,6 +121,62 @@ function rotateSphere(params) {
|
|
|
121
121
|
return opts.inverse ? compute_rotation_inverse(a, b) : compute_rotation(a, b);
|
|
122
122
|
};
|
|
123
123
|
}
|
|
124
|
+
function verticalPerspective(params) {
|
|
125
|
+
// WGS 84 spheroid
|
|
126
|
+
const semimajor = params.a;
|
|
127
|
+
const semiminor = params.b;
|
|
128
|
+
const eccen = Math.sqrt(1 - (semiminor * semiminor) / (semimajor * semimajor));
|
|
129
|
+
const radians = Math.PI / 180;
|
|
130
|
+
let { lat_0, lon_0, alt } = params;
|
|
131
|
+
const alt_0 = 0;
|
|
132
|
+
const alt_00 = 0;
|
|
133
|
+
const eccen2 = eccen * eccen;
|
|
134
|
+
lat_0 *= radians;
|
|
135
|
+
lon_0 *= radians;
|
|
136
|
+
const sin_lat_0 = Math.sin(lat_0);
|
|
137
|
+
const cos_lat_0 = Math.cos(lat_0);
|
|
138
|
+
const N1 = semimajor / Math.sqrt(1 - (eccen * sin_lat_0) ** 2);
|
|
139
|
+
let lat_g = lat_0, P = 0;
|
|
140
|
+
for (let i = 0; i < 2; i++) {
|
|
141
|
+
P = Math.cos(lat_0) / Math.cos(lat_g) * (alt + N1 + alt_00) / semimajor;
|
|
142
|
+
lat_g = lat_0 - Math.asin(N1 * eccen * eccen * sin_lat_0 * cos_lat_0 / (P * semimajor));
|
|
143
|
+
}
|
|
144
|
+
const compute_perspective = (lon, lat) => {
|
|
145
|
+
lon *= radians;
|
|
146
|
+
lat *= radians;
|
|
147
|
+
const sin_lat = Math.sin(lat);
|
|
148
|
+
const cos_lat = Math.cos(lat);
|
|
149
|
+
const N = semimajor / Math.sqrt(1 - (eccen * sin_lat) ** 2);
|
|
150
|
+
const C = (N + alt_0) / semimajor * cos_lat;
|
|
151
|
+
const S = ((N * (1 - eccen * eccen) + alt_0) / semimajor) * sin_lat;
|
|
152
|
+
const K = alt / (P * Math.cos(lat_0 - lat_g) - S * sin_lat_0 - C * cos_lat_0 * Math.cos(lon - lon_0));
|
|
153
|
+
const x = K * C * Math.sin(lon - lon_0);
|
|
154
|
+
const y = K * (P * Math.sin(lat_0 - lat_g) + S * cos_lat_0 - C * sin_lat_0 * Math.cos(lon - lon_0));
|
|
155
|
+
return [x, y];
|
|
156
|
+
};
|
|
157
|
+
const B = P * Math.cos(lat_0 - lat_g);
|
|
158
|
+
const D = P * Math.sin(lat_0 - lat_g);
|
|
159
|
+
const L = 1 - eccen2 * cos_lat_0 * cos_lat_0;
|
|
160
|
+
const G = 1 - eccen2 * sin_lat_0 * sin_lat_0;
|
|
161
|
+
const J = 2 * eccen2 * sin_lat_0 * cos_lat_0;
|
|
162
|
+
const E = 1; // If alt_0 = 0, set E = 1
|
|
163
|
+
const t = P * P * (1 - (eccen * Math.cos(lat_g)) ** 2) - E * (1 - eccen2);
|
|
164
|
+
const compute_perspective_inverse = (x, y) => {
|
|
165
|
+
const u = -2 * B * L * alt - 2 * D * G * y + B * J * y + D * J * alt;
|
|
166
|
+
const v = L * alt * alt + G * y * y - alt * J * y + (1 - eccen2) * x * x;
|
|
167
|
+
const K_prime = (-u + Math.sqrt(u * u - 4 * t * v)) / (2 * t);
|
|
168
|
+
const X = semimajor * ((B - alt / K_prime) * cos_lat_0 - (y / K_prime - D) * sin_lat_0);
|
|
169
|
+
const Y = semimajor * x / K_prime;
|
|
170
|
+
const S = (y / K_prime - D) * cos_lat_0 + (B - alt / K_prime) * sin_lat_0;
|
|
171
|
+
const lon = lon_0 + Math.atan2(Y, X);
|
|
172
|
+
const lat = Math.atan2(S, Math.sqrt((1 - eccen2) * (1 - eccen2 - S * S)));
|
|
173
|
+
return [lon / radians, lat / radians];
|
|
174
|
+
};
|
|
175
|
+
return (a, b, opts) => {
|
|
176
|
+
opts = opts === undefined ? { inverse: false } : opts;
|
|
177
|
+
return opts.inverse ? compute_perspective_inverse(a, b) : compute_perspective(a, b);
|
|
178
|
+
};
|
|
179
|
+
}
|
|
124
180
|
function mercatorXfromLng(lng) {
|
|
125
181
|
return (180 + lng) / 360;
|
|
126
182
|
}
|
|
@@ -130,7 +186,7 @@ function lngFromMercatorX(x) {
|
|
|
130
186
|
function mercatorYfromLat(lat) {
|
|
131
187
|
const sin_lat = Math.sin(lat * Math.PI / 180);
|
|
132
188
|
const y = (180 - (90 / Math.PI * Math.log((1 + sin_lat) / (1 - sin_lat)))) / 360;
|
|
133
|
-
return Math.min(
|
|
189
|
+
return Math.min(1.5, Math.max(-0.5, y));
|
|
134
190
|
}
|
|
135
191
|
function latFromMercatorY(y) {
|
|
136
192
|
return Math.atan(Math.sinh((180 - y * 360) * Math.PI / 180)) * 180 / Math.PI;
|
|
@@ -166,4 +222,4 @@ class LngLat {
|
|
|
166
222
|
return new LngLat(lngFromMercatorX(x), latFromMercatorY(y));
|
|
167
223
|
}
|
|
168
224
|
}
|
|
169
|
-
export { LngLat, lambertConformalConic, rotateSphere };
|
|
225
|
+
export { LngLat, lambertConformalConic, rotateSphere, verticalPerspective };
|
package/lib/Paintball.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { TypedArray, WebGLAnyRenderingContext } from "./AutumnTypes";
|
|
1
|
+
import { RenderMethodArg, TypedArray, WebGLAnyRenderingContext } from "./AutumnTypes";
|
|
2
|
+
import { StructuredGrid } from "./Grid";
|
|
2
3
|
import { MapLikeType } from "./Map";
|
|
3
4
|
import { PlotComponent } from "./PlotComponent";
|
|
4
5
|
import { RawScalarField } from "./RawField";
|
|
6
|
+
/** Options for {@link Paintball} components */
|
|
5
7
|
interface PaintballOptions {
|
|
6
8
|
/**
|
|
7
9
|
* The list of colors (as hex strings) to use for each member in the paintball plot. The first color corresponds to member 1, the second to member 2, etc.
|
|
@@ -20,7 +22,7 @@ interface PaintballOptions {
|
|
|
20
22
|
* of single-precision floats, this works for up to 24 members. (Technically speaking, I don't need the quotes around "bits", as they're bits of the
|
|
21
23
|
* significand of an IEEE 754 float.)
|
|
22
24
|
*/
|
|
23
|
-
declare class Paintball<ArrayType extends TypedArray, MapType extends MapLikeType> extends PlotComponent<MapType> {
|
|
25
|
+
declare class Paintball<ArrayType extends TypedArray, GridType extends StructuredGrid, MapType extends MapLikeType> extends PlotComponent<MapType> {
|
|
24
26
|
private field;
|
|
25
27
|
readonly opts: Required<PaintballOptions>;
|
|
26
28
|
private readonly color_components;
|
|
@@ -33,12 +35,12 @@ declare class Paintball<ArrayType extends TypedArray, MapType extends MapLikeTyp
|
|
|
33
35
|
* `M2` is the same thing for member 2, and `M3` and `M4` and up to `Mn` are the same thing for the rest of the members.
|
|
34
36
|
* @param opts - Options for creating the paintball plot
|
|
35
37
|
*/
|
|
36
|
-
constructor(field: RawScalarField<ArrayType>, opts?: PaintballOptions);
|
|
38
|
+
constructor(field: RawScalarField<ArrayType, GridType>, opts?: PaintballOptions);
|
|
37
39
|
/**
|
|
38
40
|
* Update the field displayed as a paintball plot
|
|
39
41
|
* @param field - The new field to display as a paintball plot
|
|
40
42
|
*/
|
|
41
|
-
updateField(field: RawScalarField<ArrayType>): Promise<void>;
|
|
43
|
+
updateField(field: RawScalarField<ArrayType, GridType>): Promise<void>;
|
|
42
44
|
/**
|
|
43
45
|
* @internal
|
|
44
46
|
* Add the paintball plot to a map.
|
|
@@ -48,7 +50,7 @@ declare class Paintball<ArrayType extends TypedArray, MapType extends MapLikeTyp
|
|
|
48
50
|
* @internal
|
|
49
51
|
* Render the paintball plot
|
|
50
52
|
*/
|
|
51
|
-
render(gl: WebGLAnyRenderingContext,
|
|
53
|
+
render(gl: WebGLAnyRenderingContext, arg: RenderMethodArg): void;
|
|
52
54
|
}
|
|
53
55
|
export default Paintball;
|
|
54
56
|
export type { PaintballOptions };
|
package/lib/Paintball.js
CHANGED
|
@@ -1,33 +1,40 @@
|
|
|
1
|
+
import { getRendererData } from "./AutumnTypes";
|
|
1
2
|
import { Color } from "./Color";
|
|
2
|
-
import { PlotComponent
|
|
3
|
+
import { PlotComponent } from "./PlotComponent";
|
|
4
|
+
import { ShaderProgramManager } from "./ShaderManager";
|
|
3
5
|
import { normalizeOptions } from "./utils";
|
|
4
|
-
import {
|
|
5
|
-
const paintball_vertex_shader_src =
|
|
6
|
+
import { WGLTexture } from "autumn-wgl";
|
|
7
|
+
const paintball_vertex_shader_src = `#version 300 es
|
|
8
|
+
|
|
6
9
|
uniform int u_offset;
|
|
7
10
|
|
|
8
|
-
|
|
9
|
-
|
|
11
|
+
in vec2 a_pos;
|
|
12
|
+
in vec2 a_tex_coord;
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
out highp vec2 v_tex_coord;
|
|
12
15
|
|
|
13
16
|
void main() {
|
|
14
17
|
float globe_width = 1.;
|
|
15
18
|
vec2 globe_offset = vec2(globe_width * float(u_offset), 0.);
|
|
16
19
|
|
|
17
|
-
gl_Position =
|
|
20
|
+
gl_Position = projectTile(a_pos.xy + globe_offset);
|
|
18
21
|
v_tex_coord = a_tex_coord;
|
|
19
22
|
}`
|
|
20
|
-
const paintball_fragment_shader_src = `#
|
|
23
|
+
const paintball_fragment_shader_src = `#version 300 es
|
|
24
|
+
|
|
25
|
+
#define MAX_N_COLORS 24
|
|
21
26
|
|
|
22
|
-
|
|
27
|
+
in highp vec2 v_tex_coord;
|
|
23
28
|
|
|
24
29
|
uniform sampler2D u_fill_sampler;
|
|
25
30
|
uniform lowp vec4 u_colors[MAX_N_COLORS];
|
|
26
31
|
uniform int u_num_colors;
|
|
27
32
|
uniform highp float u_opacity;
|
|
28
33
|
|
|
34
|
+
out highp vec4 fragColor;
|
|
35
|
+
|
|
29
36
|
void main() {
|
|
30
|
-
highp float fill_val =
|
|
37
|
+
highp float fill_val = texture(u_fill_sampler, v_tex_coord).r;
|
|
31
38
|
|
|
32
39
|
if (fill_val < 0.5) {
|
|
33
40
|
discard;
|
|
@@ -44,7 +51,7 @@ void main() {
|
|
|
44
51
|
}
|
|
45
52
|
|
|
46
53
|
color.a = color.a * u_opacity;
|
|
47
|
-
|
|
54
|
+
fragColor = color;
|
|
48
55
|
}`
|
|
49
56
|
const paintball_opt_defaults = {
|
|
50
57
|
colors: ['#000000'],
|
|
@@ -69,7 +76,7 @@ class Paintball extends PlotComponent {
|
|
|
69
76
|
super();
|
|
70
77
|
this.field = field;
|
|
71
78
|
this.opts = normalizeOptions(opts, paintball_opt_defaults);
|
|
72
|
-
this.color_components =
|
|
79
|
+
this.color_components = this.opts.colors.map(color => Color.fromHex(color).toRGBATuple()).flat();
|
|
73
80
|
this.gl_elems = null;
|
|
74
81
|
this.fill_texture = null;
|
|
75
82
|
}
|
|
@@ -83,12 +90,7 @@ class Paintball extends PlotComponent {
|
|
|
83
90
|
return;
|
|
84
91
|
const gl = this.gl_elems.gl;
|
|
85
92
|
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 2);
|
|
86
|
-
const
|
|
87
|
-
const { format, type, row_alignment } = getGLFormatTypeAlignment(gl, !(tex_data instanceof Float32Array));
|
|
88
|
-
const fill_image = { 'format': format, 'type': type,
|
|
89
|
-
'width': this.field.grid.ni, 'height': this.field.grid.nj, 'image': tex_data,
|
|
90
|
-
'mag_filter': gl.NEAREST, 'row_alignment': row_alignment,
|
|
91
|
-
};
|
|
93
|
+
const fill_image = this.field.getWGLTextureSpec(gl, gl.NEAREST);
|
|
92
94
|
if (this.fill_texture === null) {
|
|
93
95
|
this.fill_texture = new WGLTexture(gl, fill_image);
|
|
94
96
|
}
|
|
@@ -102,12 +104,12 @@ class Paintball extends PlotComponent {
|
|
|
102
104
|
*/
|
|
103
105
|
async onAdd(map, gl) {
|
|
104
106
|
gl.getExtension('OES_texture_float');
|
|
105
|
-
const program = new WGLProgram(gl, paintball_vertex_shader_src, paintball_fragment_shader_src);
|
|
106
107
|
const { vertices: verts_buf, texcoords: tex_coords_buf } = await this.field.grid.getWGLBuffers(gl);
|
|
107
108
|
const vertices = verts_buf;
|
|
108
109
|
const texcoords = tex_coords_buf;
|
|
110
|
+
const shader_manager = new ShaderProgramManager(paintball_vertex_shader_src, paintball_fragment_shader_src, []);
|
|
109
111
|
this.gl_elems = {
|
|
110
|
-
gl: gl,
|
|
112
|
+
gl: gl, shader_manager: shader_manager, vertices: vertices, texcoords: texcoords,
|
|
111
113
|
};
|
|
112
114
|
this.updateField(this.field);
|
|
113
115
|
}
|
|
@@ -115,23 +117,26 @@ class Paintball extends PlotComponent {
|
|
|
115
117
|
* @internal
|
|
116
118
|
* Render the paintball plot
|
|
117
119
|
*/
|
|
118
|
-
render(gl,
|
|
120
|
+
render(gl, arg) {
|
|
119
121
|
if (this.gl_elems === null || this.fill_texture === null)
|
|
120
122
|
return;
|
|
121
123
|
const gl_elems = this.gl_elems;
|
|
122
|
-
|
|
123
|
-
|
|
124
|
+
const render_data = getRendererData(arg);
|
|
125
|
+
const program = this.gl_elems.shader_manager.getShaderProgram(gl, render_data.shaderData);
|
|
124
126
|
// Render to framebuffer
|
|
125
|
-
|
|
127
|
+
program.use({ 'a_pos': gl_elems.vertices, 'a_tex_coord': gl_elems.texcoords }, { 'u_opacity': this.opts.opacity, 'u_colors': this.color_components, 'u_num_colors': this.opts.colors.length, 'u_offset': 0,
|
|
128
|
+
...this.gl_elems.shader_manager.getShaderUniforms(render_data) }, { 'u_fill_sampler': this.fill_texture });
|
|
126
129
|
gl.enable(gl.BLEND);
|
|
127
130
|
gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
131
|
+
program.draw();
|
|
132
|
+
if (render_data.type != 'maplibre' || !render_data.shaderData.define.includes('GLOBE')) {
|
|
133
|
+
program.setUniforms({ 'u_offset': -2 });
|
|
134
|
+
program.draw();
|
|
135
|
+
program.setUniforms({ 'u_offset': -1 });
|
|
136
|
+
program.draw();
|
|
137
|
+
program.setUniforms({ 'u_offset': 1 });
|
|
138
|
+
program.draw();
|
|
139
|
+
}
|
|
135
140
|
}
|
|
136
141
|
}
|
|
137
142
|
export default Paintball;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { RenderMethodArg, TypedArray, WebGLAnyRenderingContext } from "./AutumnTypes";
|
|
2
|
+
import { Grid } from "./Grid";
|
|
3
|
+
import { MapLikeType } from "./Map";
|
|
4
|
+
import { PlotComponent } from "./PlotComponent";
|
|
5
|
+
import { RawVectorField } from "./RawField";
|
|
6
|
+
interface ParticleTracerOptions {
|
|
7
|
+
}
|
|
8
|
+
declare class ParticleTracer<ArrayType extends TypedArray, GridType extends Grid, MapType extends MapLikeType> extends PlotComponent<MapType> {
|
|
9
|
+
field: RawVectorField<ArrayType, GridType>;
|
|
10
|
+
readonly opts: Required<ParticleTracerOptions>;
|
|
11
|
+
private gl_elems;
|
|
12
|
+
private wind_textures;
|
|
13
|
+
constructor(fields: RawVectorField<ArrayType, GridType>, opts?: ParticleTracerOptions);
|
|
14
|
+
updateField(field: RawVectorField<ArrayType, GridType>): void;
|
|
15
|
+
onAdd(map: MapType, gl: WebGLAnyRenderingContext): Promise<void>;
|
|
16
|
+
onRemove(map: MapType, gl: WebGLAnyRenderingContext): void;
|
|
17
|
+
render(gl: WebGLAnyRenderingContext, args: RenderMethodArg): void;
|
|
18
|
+
}
|
|
19
|
+
export { ParticleTracer };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { WGLTexture } from "autumn-wgl";
|
|
2
|
+
import { PlotComponent } from "./PlotComponent";
|
|
3
|
+
import { normalizeOptions } from "./utils";
|
|
4
|
+
const particle_tracer_opt_defaults = {};
|
|
5
|
+
class ParticleTracer extends PlotComponent {
|
|
6
|
+
constructor(fields, opts) {
|
|
7
|
+
super();
|
|
8
|
+
this.field = fields;
|
|
9
|
+
this.opts = normalizeOptions(opts, particle_tracer_opt_defaults);
|
|
10
|
+
this.gl_elems = null;
|
|
11
|
+
this.wind_textures = null;
|
|
12
|
+
}
|
|
13
|
+
updateField(field) {
|
|
14
|
+
this.field = field;
|
|
15
|
+
if (this.gl_elems === null)
|
|
16
|
+
return;
|
|
17
|
+
const gl = this.gl_elems.gl;
|
|
18
|
+
const { u: u_image, v: v_image } = this.field.getWGLTextureSpecs(gl, gl.NEAREST);
|
|
19
|
+
if (this.wind_textures === null) {
|
|
20
|
+
this.wind_textures = { u: new WGLTexture(gl, u_image), v: new WGLTexture(gl, v_image) };
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
this.wind_textures.u.setImageData(u_image);
|
|
24
|
+
this.wind_textures.v.setImageData(v_image);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async onAdd(map, gl) {
|
|
28
|
+
const { rotation: proj_rotation_tex } = this.field.grid.getVectorRotationTexture(gl, this.field.relative_to == 'earth');
|
|
29
|
+
this.gl_elems = { gl: gl, proj_rot_texture: proj_rotation_tex };
|
|
30
|
+
this.updateField(this.field);
|
|
31
|
+
}
|
|
32
|
+
onRemove(map, gl) {
|
|
33
|
+
}
|
|
34
|
+
render(gl, args) {
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export { ParticleTracer };
|
package/lib/PlotComponent.d.ts
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
import * as Comlink from 'comlink';
|
|
2
2
|
import { MapLikeType } from './Map';
|
|
3
|
-
import { WebGLAnyRenderingContext } from './AutumnTypes';
|
|
3
|
+
import { RenderMethodArg, TypedArrayStr, WebGLAnyRenderingContext } from './AutumnTypes';
|
|
4
4
|
declare const layer_worker: Comlink.Remote<{
|
|
5
|
-
makeBBElements: (field_lats: Float32Array, field_lons: Float32Array,
|
|
5
|
+
makeBBElements: (field_lats: Float32Array, field_lons: Float32Array, min_zoom: Uint8Array, field_ni: number, field_nj: number, map_max_zoom: number) => {
|
|
6
6
|
pts: Float32Array;
|
|
7
7
|
tex_coords: Float32Array;
|
|
8
8
|
};
|
|
9
9
|
makeDomainVerticesAndTexCoords: (field_lats: Float32Array, field_lons: Float32Array, field_ni: number, field_nj: number, texcoord_margin_r: number, texcoord_margin_s: number) => {
|
|
10
10
|
vertices: Float32Array;
|
|
11
11
|
tex_coords: Float32Array;
|
|
12
|
-
grid_cell_size: Float32Array;
|
|
13
12
|
};
|
|
14
13
|
makePolyLines: (lines: import("./AutumnTypes").LineData[]) => import("./AutumnTypes").Polyline;
|
|
15
14
|
}>;
|
|
15
|
+
/** Base class for all plot components */
|
|
16
16
|
declare abstract class PlotComponent<MapType extends MapLikeType> {
|
|
17
17
|
abstract onAdd(map: MapType, gl: WebGLAnyRenderingContext): Promise<void>;
|
|
18
|
-
abstract render(gl: WebGLAnyRenderingContext,
|
|
18
|
+
abstract render(gl: WebGLAnyRenderingContext, arg: RenderMethodArg): void;
|
|
19
19
|
}
|
|
20
|
-
declare function getGLFormatTypeAlignment(gl: WebGLAnyRenderingContext,
|
|
21
|
-
format: 33325 | 33326 | 6409;
|
|
22
|
-
type: 36193 | 5131 | 5126;
|
|
20
|
+
declare function getGLFormatTypeAlignment(gl: WebGLAnyRenderingContext, array_dtype: TypedArrayStr): {
|
|
21
|
+
format: 33321 | 33325 | 33326 | 6409;
|
|
22
|
+
type: 36193 | 5131 | 5121 | 5126;
|
|
23
23
|
row_alignment: number;
|
|
24
24
|
};
|
|
25
25
|
export { PlotComponent, layer_worker, getGLFormatTypeAlignment };
|
package/lib/PlotComponent.js
CHANGED
|
@@ -3,12 +3,13 @@ import { getOS } from "./utils";
|
|
|
3
3
|
import { isWebGL2Ctx } from './AutumnTypes';
|
|
4
4
|
const worker = new Worker(new URL('./PlotLayer.worker', import.meta.url));
|
|
5
5
|
const layer_worker = Comlink.wrap(worker);
|
|
6
|
+
/** Base class for all plot components */
|
|
6
7
|
class PlotComponent {
|
|
7
8
|
}
|
|
8
|
-
function getGLFormatTypeAlignment(gl,
|
|
9
|
+
function getGLFormatTypeAlignment(gl, array_dtype) {
|
|
9
10
|
let format, type, row_alignment;
|
|
10
11
|
const is_webgl2 = isWebGL2Ctx(gl);
|
|
11
|
-
if (
|
|
12
|
+
if (array_dtype == 'float16') {
|
|
12
13
|
const ext = gl.getExtension('OES_texture_half_float');
|
|
13
14
|
const ext_lin = gl.getExtension('OES_texture_half_float_linear');
|
|
14
15
|
if (is_webgl2) {
|
|
@@ -23,7 +24,7 @@ function getGLFormatTypeAlignment(gl, is_float16) {
|
|
|
23
24
|
}
|
|
24
25
|
row_alignment = 2;
|
|
25
26
|
}
|
|
26
|
-
else {
|
|
27
|
+
else if (array_dtype == 'float32') {
|
|
27
28
|
const ext = gl.getExtension('OES_texture_float');
|
|
28
29
|
const ext_lin = gl.getExtension('OES_texture_float_linear');
|
|
29
30
|
// As of 11/3/2023, Safari/WebKit on iOS reports as supporting float textures,
|
|
@@ -39,6 +40,11 @@ function getGLFormatTypeAlignment(gl, is_float16) {
|
|
|
39
40
|
type = gl.FLOAT;
|
|
40
41
|
row_alignment = 4;
|
|
41
42
|
}
|
|
43
|
+
else {
|
|
44
|
+
format = is_webgl2 ? gl.R8 : gl.LUMINANCE;
|
|
45
|
+
type = gl.UNSIGNED_BYTE;
|
|
46
|
+
row_alignment = 1;
|
|
47
|
+
}
|
|
42
48
|
return { format: format, type: type, row_alignment: row_alignment };
|
|
43
49
|
}
|
|
44
50
|
export { PlotComponent, layer_worker, getGLFormatTypeAlignment };
|