autumnplot-gl 2.2.3 → 3.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 +65 -8
- package/dist/110.autumnplot-gl.js +2 -0
- package/dist/110.autumnplot-gl.js.map +1 -0
- package/dist/autumnplot-gl.js +3 -0
- package/dist/autumnplot-gl.js.LICENSE.txt +12 -0
- package/dist/autumnplot-gl.js.map +1 -0
- package/dist/marchingsquares.wasm +0 -0
- package/lib/AutumnTypes.d.ts +14 -13
- package/lib/Barbs.d.ts +8 -3
- package/lib/Barbs.js +14 -1
- package/lib/BillboardCollection.d.ts +11 -7
- package/lib/BillboardCollection.js +52 -30
- package/lib/ColorBar.js +51 -7
- package/lib/Colormap.d.ts +14 -3
- package/lib/Colormap.js +63 -12
- package/lib/Contour.d.ts +73 -16
- package/lib/Contour.js +175 -146
- package/lib/ContourCreator.d.ts +18 -0
- package/lib/ContourCreator.js +59 -0
- package/lib/Fill.d.ts +17 -5
- package/lib/Fill.js +60 -37
- package/lib/Grid.d.ts +167 -0
- package/lib/Grid.js +339 -0
- package/lib/Hodographs.d.ts +13 -5
- package/lib/Hodographs.js +52 -67
- package/lib/Map.d.ts +14 -3
- package/lib/Map.js +9 -0
- package/lib/Paintball.d.ts +9 -3
- package/lib/Paintball.js +28 -10
- package/lib/PlotComponent.d.ts +5 -5
- package/lib/PlotLayer.d.ts +12 -12
- package/lib/PlotLayer.js +16 -14
- package/lib/PlotLayer.worker.d.ts +2 -2
- package/lib/PlotLayer.worker.js +102 -66
- package/lib/PolylineCollection.d.ts +20 -9
- package/lib/PolylineCollection.js +158 -32
- package/lib/RawField.d.ts +11 -167
- package/lib/RawField.js +37 -383
- package/lib/TextCollection.d.ts +31 -0
- package/lib/TextCollection.js +295 -0
- package/lib/cpp/marchingsquares.d.ts +6 -0
- package/lib/cpp/marchingsquares.js +4152 -0
- package/lib/cpp/marchingsquares.wasm +0 -0
- package/lib/cpp/marchingsquares_embind.d.ts +47 -0
- package/lib/index.d.ts +13 -6
- package/lib/index.js +12 -3
- package/lib/utils.d.ts +5 -3
- package/lib/utils.js +17 -6
- package/package.json +14 -9
package/lib/RawField.d.ts
CHANGED
|
@@ -1,170 +1,13 @@
|
|
|
1
1
|
import { Float16Array } from "@petamoriken/float16";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
lons: Float32Array;
|
|
6
|
-
lats: Float32Array;
|
|
7
|
-
}
|
|
8
|
-
interface GridCoords {
|
|
9
|
-
x: Float32Array;
|
|
10
|
-
y: Float32Array;
|
|
11
|
-
}
|
|
12
|
-
type GridType = 'latlon' | 'latlonrot' | 'lcc';
|
|
13
|
-
declare abstract class Grid {
|
|
14
|
-
readonly type: GridType;
|
|
15
|
-
readonly ni: number;
|
|
16
|
-
readonly nj: number;
|
|
17
|
-
readonly is_conformal: boolean;
|
|
18
|
-
private readonly buffer_cache;
|
|
19
|
-
private readonly billboard_buffer_cache;
|
|
20
|
-
constructor(type: GridType, is_conformal: boolean, ni: number, nj: number);
|
|
21
|
-
abstract copy(opts?: {
|
|
22
|
-
ni?: number;
|
|
23
|
-
nj?: number;
|
|
24
|
-
}): Grid;
|
|
25
|
-
abstract getEarthCoords(): EarthCoords;
|
|
26
|
-
abstract getGridCoords(): GridCoords;
|
|
27
|
-
abstract transform(x: number, y: number, opts?: {
|
|
28
|
-
inverse?: boolean;
|
|
29
|
-
}): [number, number];
|
|
30
|
-
abstract getThinnedGrid(thin_x: number, thin_y: number): Grid;
|
|
31
|
-
getWGLBuffers(gl: WebGLAnyRenderingContext): Promise<{
|
|
32
|
-
vertices: WGLBuffer;
|
|
33
|
-
texcoords: WGLBuffer;
|
|
34
|
-
cellsize: WGLBuffer;
|
|
35
|
-
}>;
|
|
36
|
-
getWGLBillboardBuffers(gl: WebGLAnyRenderingContext, thin_fac: number, max_zoom: number): Promise<{
|
|
37
|
-
vertices: WGLBuffer;
|
|
38
|
-
texcoords: WGLBuffer;
|
|
39
|
-
}>;
|
|
40
|
-
}
|
|
41
|
-
/** A plate carree (a.k.a. lat/lon) grid with uniform grid spacing */
|
|
42
|
-
declare class PlateCarreeGrid extends Grid {
|
|
43
|
-
readonly ll_lon: number;
|
|
44
|
-
readonly ll_lat: number;
|
|
45
|
-
readonly ur_lon: number;
|
|
46
|
-
readonly ur_lat: number;
|
|
47
|
-
private readonly ll_cache;
|
|
48
|
-
private readonly gc_cache;
|
|
49
|
-
/**
|
|
50
|
-
* Create a plate carree grid
|
|
51
|
-
* @param ni - The number of grid points in the i (longitude) direction
|
|
52
|
-
* @param nj - The number of grid points in the j (latitude) direction
|
|
53
|
-
* @param ll_lon - The longitude of the lower left corner of the grid
|
|
54
|
-
* @param ll_lat - The latitude of the lower left corner of the grid
|
|
55
|
-
* @param ur_lon - The longitude of the upper right corner of the grid
|
|
56
|
-
* @param ur_lat - The latitude of the upper right corner of the grid
|
|
57
|
-
*/
|
|
58
|
-
constructor(ni: number, nj: number, ll_lon: number, ll_lat: number, ur_lon: number, ur_lat: number);
|
|
59
|
-
copy(opts?: {
|
|
60
|
-
ni?: number;
|
|
61
|
-
nj?: number;
|
|
62
|
-
ll_lon?: number;
|
|
63
|
-
ll_lat?: number;
|
|
64
|
-
ur_lon?: number;
|
|
65
|
-
ur_lat?: number;
|
|
66
|
-
}): PlateCarreeGrid;
|
|
67
|
-
/**
|
|
68
|
-
* Get a list of longitudes and latitudes on the grid (internal method)
|
|
69
|
-
*/
|
|
70
|
-
getEarthCoords(): EarthCoords;
|
|
71
|
-
getGridCoords(): GridCoords;
|
|
72
|
-
transform(x: number, y: number, opts?: {
|
|
73
|
-
inverse?: boolean;
|
|
74
|
-
}): [number, number];
|
|
75
|
-
getThinnedGrid(thin_x: number, thin_y: number): PlateCarreeGrid;
|
|
76
|
-
}
|
|
77
|
-
/** A rotated lat-lon (plate carree) grid with uniform grid spacing */
|
|
78
|
-
declare class PlateCarreeRotatedGrid extends Grid {
|
|
79
|
-
readonly np_lon: number;
|
|
80
|
-
readonly np_lat: number;
|
|
81
|
-
readonly lon_shift: number;
|
|
82
|
-
readonly ll_lon: number;
|
|
83
|
-
readonly ll_lat: number;
|
|
84
|
-
readonly ur_lon: number;
|
|
85
|
-
readonly ur_lat: number;
|
|
86
|
-
private readonly llrot;
|
|
87
|
-
private readonly ll_cache;
|
|
88
|
-
private readonly gc_cache;
|
|
89
|
-
/**
|
|
90
|
-
* Create a Lambert conformal conic grid
|
|
91
|
-
* @param ni - The number of grid points in the i (longitude) direction
|
|
92
|
-
* @param nj - The number of grid points in the j (latitude) direction
|
|
93
|
-
* @param np_lon - The longitude of the north pole for the rotated grid
|
|
94
|
-
* @param np_lat - The latitude of the north pole for the rotated grid
|
|
95
|
-
* @param lon_shift - The angle around the rotated north pole to shift the central meridian
|
|
96
|
-
* @param ll_lon - The longitude of the lower left corner of the grid (on the rotated earth)
|
|
97
|
-
* @param ll_lat - The latitude of the lower left corner of the grid (on the rotated earth)
|
|
98
|
-
* @param ur_lon - The longitude of the upper right corner of the grid (on the rotated earth)
|
|
99
|
-
* @param ur_lat - The latitude of the upper right corner of the grid (on the rotated earth)
|
|
100
|
-
*/
|
|
101
|
-
constructor(ni: number, nj: number, np_lon: number, np_lat: number, lon_shift: number, ll_lon: number, ll_lat: number, ur_lon: number, ur_lat: number);
|
|
102
|
-
copy(opts?: {
|
|
103
|
-
ni?: number;
|
|
104
|
-
nj?: number;
|
|
105
|
-
ll_lon?: number;
|
|
106
|
-
ll_lat?: number;
|
|
107
|
-
ur_lon?: number;
|
|
108
|
-
ur_lat?: number;
|
|
109
|
-
}): PlateCarreeRotatedGrid;
|
|
110
|
-
/**
|
|
111
|
-
* Get a list of longitudes and latitudes on the grid (internal method)
|
|
112
|
-
*/
|
|
113
|
-
getEarthCoords(): EarthCoords;
|
|
114
|
-
getGridCoords(): GridCoords;
|
|
115
|
-
transform(x: number, y: number, opts?: {
|
|
116
|
-
inverse?: boolean;
|
|
117
|
-
}): [number, number];
|
|
118
|
-
getThinnedGrid(thin_x: number, thin_y: number): PlateCarreeRotatedGrid;
|
|
119
|
-
}
|
|
120
|
-
/** A Lambert conformal conic grid with uniform grid spacing */
|
|
121
|
-
declare class LambertGrid extends Grid {
|
|
122
|
-
readonly lon_0: number;
|
|
123
|
-
readonly lat_0: number;
|
|
124
|
-
readonly lat_std: [number, number];
|
|
125
|
-
readonly ll_x: number;
|
|
126
|
-
readonly ll_y: number;
|
|
127
|
-
readonly ur_x: number;
|
|
128
|
-
readonly ur_y: number;
|
|
129
|
-
private readonly lcc;
|
|
130
|
-
private readonly ll_cache;
|
|
131
|
-
private readonly gc_cache;
|
|
132
|
-
/**
|
|
133
|
-
* Create a Lambert conformal conic grid
|
|
134
|
-
* @param ni - The number of grid points in the i (longitude) direction
|
|
135
|
-
* @param nj - The number of grid points in the j (latitude) direction
|
|
136
|
-
* @param lon_0 - The standard longitude for the projection; this is also the center longitude for the projection
|
|
137
|
-
* @param lat_0 - The center latitude for the projection
|
|
138
|
-
* @param lat_std - The standard latitudes for the projection
|
|
139
|
-
* @param ll_x - The x coordinate in projection space of the lower-left corner of the grid
|
|
140
|
-
* @param ll_y - The y coordinate in projection space of the lower-left corner of the grid
|
|
141
|
-
* @param ur_x - The x coordinate in projection space of the upper-right corner of the grid
|
|
142
|
-
* @param ur_y - The y coordinate in projection space of the upper-right corner of the grid
|
|
143
|
-
*/
|
|
144
|
-
constructor(ni: number, nj: number, lon_0: number, lat_0: number, lat_std: [number, number], ll_x: number, ll_y: number, ur_x: number, ur_y: number);
|
|
145
|
-
copy(opts?: {
|
|
146
|
-
ni?: number;
|
|
147
|
-
nj?: number;
|
|
148
|
-
ll_x?: number;
|
|
149
|
-
ll_y?: number;
|
|
150
|
-
ur_x?: number;
|
|
151
|
-
ur_y?: number;
|
|
152
|
-
}): LambertGrid;
|
|
153
|
-
/**
|
|
154
|
-
* Get a list of longitudes and latitudes on the grid (internal method)
|
|
155
|
-
*/
|
|
156
|
-
getEarthCoords(): EarthCoords;
|
|
157
|
-
getGridCoords(): GridCoords;
|
|
158
|
-
transform(x: number, y: number, opts?: {
|
|
159
|
-
inverse?: boolean;
|
|
160
|
-
}): [number, number];
|
|
161
|
-
getThinnedGrid(thin_x: number, thin_y: number): LambertGrid;
|
|
162
|
-
}
|
|
2
|
+
import { ContourData, TypedArray, WindProfile } from "./AutumnTypes";
|
|
3
|
+
import { FieldContourOpts } from "./ContourCreator";
|
|
4
|
+
import { Grid } from "./Grid";
|
|
163
5
|
type TextureDataType<ArrayType> = ArrayType extends Float32Array ? Float32Array : Uint16Array;
|
|
164
6
|
/** A class representing a raw 2D field of gridded data, such as height or u wind. */
|
|
165
7
|
declare class RawScalarField<ArrayType extends TypedArray> {
|
|
166
8
|
readonly grid: Grid;
|
|
167
9
|
readonly data: ArrayType;
|
|
10
|
+
private readonly contour_cache;
|
|
168
11
|
/**
|
|
169
12
|
* Create a data field.
|
|
170
13
|
* @param grid - The grid on which the data are defined
|
|
@@ -173,8 +16,7 @@ declare class RawScalarField<ArrayType extends TypedArray> {
|
|
|
173
16
|
constructor(grid: Grid, data: ArrayType);
|
|
174
17
|
/** @internal */
|
|
175
18
|
getTextureData(): TextureDataType<ArrayType>;
|
|
176
|
-
|
|
177
|
-
getThinnedField(thin_x: number, thin_y: number): RawScalarField<ArrayType>;
|
|
19
|
+
getContours(opts: FieldContourOpts): Promise<ContourData>;
|
|
178
20
|
/**
|
|
179
21
|
* Create a new field by aggregating a number of fields using a specific function
|
|
180
22
|
* @param func - A function that will be applied each element of the field. It should take the same number of arguments as fields you have and return a single number.
|
|
@@ -199,7 +41,6 @@ declare class RawVectorField<ArrayType extends TypedArray> {
|
|
|
199
41
|
readonly u: RawScalarField<ArrayType>;
|
|
200
42
|
readonly v: RawScalarField<ArrayType>;
|
|
201
43
|
readonly relative_to: VectorRelativeTo;
|
|
202
|
-
private readonly rotate_cache;
|
|
203
44
|
/**
|
|
204
45
|
* Create a vector field.
|
|
205
46
|
* @param grid - The grid on which the vector components are defined
|
|
@@ -208,9 +49,12 @@ declare class RawVectorField<ArrayType extends TypedArray> {
|
|
|
208
49
|
* @param opts - Options for creating the vector field.
|
|
209
50
|
*/
|
|
210
51
|
constructor(grid: Grid, u: ArrayType, v: ArrayType, opts?: RawVectorFieldOptions);
|
|
52
|
+
getTextureData(): {
|
|
53
|
+
u: TextureDataType<ArrayType>;
|
|
54
|
+
v: TextureDataType<ArrayType>;
|
|
55
|
+
};
|
|
211
56
|
getThinnedField(thin_x: number, thin_y: number): RawVectorField<ArrayType>;
|
|
212
57
|
get grid(): Grid;
|
|
213
|
-
toEarthRelative(): RawVectorField<ArrayType>;
|
|
214
58
|
}
|
|
215
59
|
/** A class grid of wind profiles */
|
|
216
60
|
declare class RawProfileField {
|
|
@@ -225,5 +69,5 @@ declare class RawProfileField {
|
|
|
225
69
|
/** Get the gridded storm motion vector field (internal method) */
|
|
226
70
|
getStormMotionGrid(): RawVectorField<Float16Array>;
|
|
227
71
|
}
|
|
228
|
-
export { RawScalarField, RawVectorField, RawProfileField
|
|
229
|
-
export type {
|
|
72
|
+
export { RawScalarField, RawVectorField, RawProfileField };
|
|
73
|
+
export type { RawVectorFieldOptions, VectorRelativeTo, TextureDataType };
|
package/lib/RawField.js
CHANGED
|
@@ -1,313 +1,6 @@
|
|
|
1
1
|
import { Float16Array } from "@petamoriken/float16";
|
|
2
|
-
import {
|
|
3
|
-
import { layer_worker } from "./PlotComponent";
|
|
2
|
+
import { contourCreator } from "./ContourCreator";
|
|
4
3
|
import { Cache, zip } from "./utils";
|
|
5
|
-
import { WGLBuffer } from "autumn-wgl";
|
|
6
|
-
async function makeWGLDomainBuffers(gl, grid, native_grid) {
|
|
7
|
-
native_grid = native_grid !== undefined ? native_grid : grid;
|
|
8
|
-
const texcoord_margin_r = 1 / (2 * native_grid.ni);
|
|
9
|
-
const texcoord_margin_s = 1 / (2 * native_grid.nj);
|
|
10
|
-
const grid_cell_size_multiplier = (grid.ni * grid.nj) / (native_grid.ni * native_grid.nj);
|
|
11
|
-
const { lats: field_lats, lons: field_lons } = grid.getEarthCoords();
|
|
12
|
-
const domain_coords = await layer_worker.makeDomainVerticesAndTexCoords(field_lats, field_lons, grid.ni, grid.nj, texcoord_margin_r, texcoord_margin_s);
|
|
13
|
-
for (let icd = 0; icd < domain_coords['grid_cell_size'].length; icd++) {
|
|
14
|
-
domain_coords['grid_cell_size'][icd] *= grid_cell_size_multiplier;
|
|
15
|
-
}
|
|
16
|
-
const vertices = new WGLBuffer(gl, domain_coords['vertices'], 2, gl.TRIANGLE_STRIP);
|
|
17
|
-
const texcoords = new WGLBuffer(gl, domain_coords['tex_coords'], 2, gl.TRIANGLE_STRIP);
|
|
18
|
-
const grid_cell_size = new WGLBuffer(gl, domain_coords['grid_cell_size'], 1, gl.TRIANGLE_STRIP);
|
|
19
|
-
return { 'vertices': vertices, 'texcoords': texcoords, 'cellsize': grid_cell_size };
|
|
20
|
-
}
|
|
21
|
-
async function makeWGLBillboardBuffers(gl, grid, thin_fac, max_zoom) {
|
|
22
|
-
const { lats: field_lats, lons: field_lons } = grid.getEarthCoords();
|
|
23
|
-
const bb_elements = await layer_worker.makeBBElements(field_lats, field_lons, grid.ni, grid.nj, thin_fac, max_zoom);
|
|
24
|
-
const vertices = new WGLBuffer(gl, bb_elements['pts'], 3, gl.TRIANGLE_STRIP);
|
|
25
|
-
const texcoords = new WGLBuffer(gl, bb_elements['tex_coords'], 2, gl.TRIANGLE_STRIP);
|
|
26
|
-
return { 'vertices': vertices, 'texcoords': texcoords };
|
|
27
|
-
}
|
|
28
|
-
class Grid {
|
|
29
|
-
constructor(type, is_conformal, ni, nj) {
|
|
30
|
-
this.type = type;
|
|
31
|
-
this.is_conformal = is_conformal;
|
|
32
|
-
this.ni = ni;
|
|
33
|
-
this.nj = nj;
|
|
34
|
-
this.buffer_cache = new Cache((gl) => {
|
|
35
|
-
const new_ni = Math.max(Math.floor(this.ni / 50), 20);
|
|
36
|
-
const new_nj = Math.max(Math.floor(this.nj / 50), 20);
|
|
37
|
-
return makeWGLDomainBuffers(gl, this.copy({ ni: new_ni, nj: new_nj }), this);
|
|
38
|
-
});
|
|
39
|
-
this.billboard_buffer_cache = new Cache((gl, thin_fac, max_zoom) => {
|
|
40
|
-
return makeWGLBillboardBuffers(gl, this, thin_fac, max_zoom);
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
async getWGLBuffers(gl) {
|
|
44
|
-
return await this.buffer_cache.getValue(gl);
|
|
45
|
-
}
|
|
46
|
-
async getWGLBillboardBuffers(gl, thin_fac, max_zoom) {
|
|
47
|
-
return await this.billboard_buffer_cache.getValue(gl, thin_fac, max_zoom);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
/** A plate carree (a.k.a. lat/lon) grid with uniform grid spacing */
|
|
51
|
-
class PlateCarreeGrid extends Grid {
|
|
52
|
-
/**
|
|
53
|
-
* Create a plate carree grid
|
|
54
|
-
* @param ni - The number of grid points in the i (longitude) direction
|
|
55
|
-
* @param nj - The number of grid points in the j (latitude) direction
|
|
56
|
-
* @param ll_lon - The longitude of the lower left corner of the grid
|
|
57
|
-
* @param ll_lat - The latitude of the lower left corner of the grid
|
|
58
|
-
* @param ur_lon - The longitude of the upper right corner of the grid
|
|
59
|
-
* @param ur_lat - The latitude of the upper right corner of the grid
|
|
60
|
-
*/
|
|
61
|
-
constructor(ni, nj, ll_lon, ll_lat, ur_lon, ur_lat) {
|
|
62
|
-
super('latlon', true, ni, nj);
|
|
63
|
-
this.ll_lon = ll_lon;
|
|
64
|
-
this.ll_lat = ll_lat;
|
|
65
|
-
this.ur_lon = ur_lon;
|
|
66
|
-
this.ur_lat = ur_lat;
|
|
67
|
-
const dlon = (this.ur_lon - this.ll_lon) / (this.ni - 1);
|
|
68
|
-
const dlat = (this.ur_lat - this.ll_lat) / (this.nj - 1);
|
|
69
|
-
this.ll_cache = new Cache(() => {
|
|
70
|
-
const lons = new Float32Array(this.ni * this.nj);
|
|
71
|
-
const lats = new Float32Array(this.ni * this.nj);
|
|
72
|
-
for (let i = 0; i < this.ni; i++) {
|
|
73
|
-
for (let j = 0; j < this.nj; j++) {
|
|
74
|
-
const idx = i + j * this.ni;
|
|
75
|
-
lons[idx] = this.ll_lon + i * dlon;
|
|
76
|
-
lats[idx] = this.ll_lat + j * dlat;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
return { 'lons': lons, 'lats': lats };
|
|
80
|
-
});
|
|
81
|
-
this.gc_cache = new Cache(() => {
|
|
82
|
-
const x = new Float32Array(this.ni);
|
|
83
|
-
const y = new Float32Array(this.nj);
|
|
84
|
-
for (let i = 0; i < this.ni; i++) {
|
|
85
|
-
x[i] = this.ll_lon + i * dlon;
|
|
86
|
-
}
|
|
87
|
-
for (let j = 0; j < this.nj; j++) {
|
|
88
|
-
y[j] = this.ll_lat + j * dlat;
|
|
89
|
-
}
|
|
90
|
-
return { x: x, y: y };
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
copy(opts) {
|
|
94
|
-
opts = opts !== undefined ? opts : {};
|
|
95
|
-
const ni = opts.ni !== undefined ? opts.ni : this.ni;
|
|
96
|
-
const nj = opts.nj !== undefined ? opts.nj : this.nj;
|
|
97
|
-
const ll_lon = opts.ll_lon !== undefined ? opts.ll_lon : this.ll_lon;
|
|
98
|
-
const ll_lat = opts.ll_lat !== undefined ? opts.ll_lat : this.ll_lat;
|
|
99
|
-
const ur_lon = opts.ur_lon !== undefined ? opts.ur_lon : this.ur_lon;
|
|
100
|
-
const ur_lat = opts.ur_lat !== undefined ? opts.ur_lat : this.ur_lat;
|
|
101
|
-
return new PlateCarreeGrid(ni, nj, ll_lon, ll_lat, ur_lon, ur_lat);
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Get a list of longitudes and latitudes on the grid (internal method)
|
|
105
|
-
*/
|
|
106
|
-
getEarthCoords() {
|
|
107
|
-
return this.ll_cache.getValue();
|
|
108
|
-
}
|
|
109
|
-
getGridCoords() {
|
|
110
|
-
return this.gc_cache.getValue();
|
|
111
|
-
}
|
|
112
|
-
transform(x, y, opts) {
|
|
113
|
-
return [x, y];
|
|
114
|
-
}
|
|
115
|
-
getThinnedGrid(thin_x, thin_y) {
|
|
116
|
-
const dlon = (this.ur_lon - this.ll_lon) / this.ni;
|
|
117
|
-
const dlat = (this.ur_lat - this.ll_lat) / this.nj;
|
|
118
|
-
const ni = Math.ceil(this.ni / thin_x);
|
|
119
|
-
const nj = Math.ceil(this.nj / thin_y);
|
|
120
|
-
const ni_remove = (this.ni - 1) % thin_x;
|
|
121
|
-
const nj_remove = (this.nj - 1) % thin_y;
|
|
122
|
-
const ll_lon = this.ll_lon;
|
|
123
|
-
const ll_lat = this.ll_lat;
|
|
124
|
-
const ur_lon = this.ur_lon - ni_remove * dlon;
|
|
125
|
-
const ur_lat = this.ur_lat - nj_remove * dlat;
|
|
126
|
-
return new PlateCarreeGrid(ni, nj, ll_lon, ll_lat, ur_lon, ur_lat);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
/** A rotated lat-lon (plate carree) grid with uniform grid spacing */
|
|
130
|
-
class PlateCarreeRotatedGrid extends Grid {
|
|
131
|
-
/**
|
|
132
|
-
* Create a Lambert conformal conic grid
|
|
133
|
-
* @param ni - The number of grid points in the i (longitude) direction
|
|
134
|
-
* @param nj - The number of grid points in the j (latitude) direction
|
|
135
|
-
* @param np_lon - The longitude of the north pole for the rotated grid
|
|
136
|
-
* @param np_lat - The latitude of the north pole for the rotated grid
|
|
137
|
-
* @param lon_shift - The angle around the rotated north pole to shift the central meridian
|
|
138
|
-
* @param ll_lon - The longitude of the lower left corner of the grid (on the rotated earth)
|
|
139
|
-
* @param ll_lat - The latitude of the lower left corner of the grid (on the rotated earth)
|
|
140
|
-
* @param ur_lon - The longitude of the upper right corner of the grid (on the rotated earth)
|
|
141
|
-
* @param ur_lat - The latitude of the upper right corner of the grid (on the rotated earth)
|
|
142
|
-
*/
|
|
143
|
-
constructor(ni, nj, np_lon, np_lat, lon_shift, ll_lon, ll_lat, ur_lon, ur_lat) {
|
|
144
|
-
super('latlonrot', true, ni, nj);
|
|
145
|
-
this.np_lon = np_lon;
|
|
146
|
-
this.np_lat = np_lat;
|
|
147
|
-
this.lon_shift = lon_shift;
|
|
148
|
-
this.ll_lon = ll_lon;
|
|
149
|
-
this.ll_lat = ll_lat;
|
|
150
|
-
this.ur_lon = ur_lon;
|
|
151
|
-
this.ur_lat = ur_lat;
|
|
152
|
-
this.llrot = rotateSphere({ np_lon: np_lon, np_lat: np_lat, lon_shift: lon_shift });
|
|
153
|
-
const dlon = (this.ur_lon - this.ll_lon) / (this.ni - 1);
|
|
154
|
-
const dlat = (this.ur_lat - this.ll_lat) / (this.nj - 1);
|
|
155
|
-
this.ll_cache = new Cache(() => {
|
|
156
|
-
const lons = new Float32Array(this.ni * this.nj);
|
|
157
|
-
const lats = new Float32Array(this.ni * this.nj);
|
|
158
|
-
for (let i = 0; i < this.ni; i++) {
|
|
159
|
-
const lon_p = this.ll_lon + i * dlon;
|
|
160
|
-
for (let j = 0; j < this.nj; j++) {
|
|
161
|
-
const lat_p = this.ll_lat + j * dlat;
|
|
162
|
-
const [lon, lat] = this.llrot(lon_p, lat_p);
|
|
163
|
-
const idx = i + j * this.ni;
|
|
164
|
-
lons[idx] = lon;
|
|
165
|
-
lats[idx] = lat;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
return { lons: lons, lats: lats };
|
|
169
|
-
});
|
|
170
|
-
this.gc_cache = new Cache(() => {
|
|
171
|
-
const x = new Float32Array(this.ni);
|
|
172
|
-
const y = new Float32Array(this.nj);
|
|
173
|
-
for (let i = 0; i < this.ni; i++) {
|
|
174
|
-
x[i] = this.ll_lon + i * dlon;
|
|
175
|
-
}
|
|
176
|
-
for (let j = 0; j < this.nj; j++) {
|
|
177
|
-
y[j] = this.ll_lat + j * dlat;
|
|
178
|
-
}
|
|
179
|
-
return { x: x, y: y };
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
copy(opts) {
|
|
183
|
-
opts = opts !== undefined ? opts : {};
|
|
184
|
-
const ni = opts.ni !== undefined ? opts.ni : this.ni;
|
|
185
|
-
const nj = opts.nj !== undefined ? opts.nj : this.nj;
|
|
186
|
-
const ll_lon = opts.ll_lon !== undefined ? opts.ll_lon : this.ll_lon;
|
|
187
|
-
const ll_lat = opts.ll_lat !== undefined ? opts.ll_lat : this.ll_lat;
|
|
188
|
-
const ur_lon = opts.ur_lon !== undefined ? opts.ur_lon : this.ur_lon;
|
|
189
|
-
const ur_lat = opts.ur_lat !== undefined ? opts.ur_lat : this.ur_lat;
|
|
190
|
-
return new PlateCarreeRotatedGrid(ni, nj, this.np_lon, this.np_lat, this.lon_shift, ll_lon, ll_lat, ur_lon, ur_lat);
|
|
191
|
-
}
|
|
192
|
-
/**
|
|
193
|
-
* Get a list of longitudes and latitudes on the grid (internal method)
|
|
194
|
-
*/
|
|
195
|
-
getEarthCoords() {
|
|
196
|
-
return this.ll_cache.getValue();
|
|
197
|
-
}
|
|
198
|
-
getGridCoords() {
|
|
199
|
-
return this.gc_cache.getValue();
|
|
200
|
-
}
|
|
201
|
-
transform(x, y, opts) {
|
|
202
|
-
opts = opts === undefined ? {} : opts;
|
|
203
|
-
const inverse = 'inverse' in opts ? opts.inverse : false;
|
|
204
|
-
return this.llrot(x, y, { inverse: !inverse });
|
|
205
|
-
}
|
|
206
|
-
getThinnedGrid(thin_x, thin_y) {
|
|
207
|
-
const dlon = (this.ur_lon - this.ll_lon) / this.ni;
|
|
208
|
-
const dlat = (this.ur_lat - this.ll_lat) / this.nj;
|
|
209
|
-
const ni = Math.ceil(this.ni / thin_x);
|
|
210
|
-
const nj = Math.ceil(this.nj / thin_y);
|
|
211
|
-
const ni_remove = (this.ni - 1) % thin_x;
|
|
212
|
-
const nj_remove = (this.nj - 1) % thin_y;
|
|
213
|
-
const ll_lon = this.ll_lon;
|
|
214
|
-
const ll_lat = this.ll_lat;
|
|
215
|
-
const ur_lon = this.ur_lon - ni_remove * dlon;
|
|
216
|
-
const ur_lat = this.ur_lat - nj_remove * dlat;
|
|
217
|
-
return new PlateCarreeRotatedGrid(ni, nj, this.np_lon, this.np_lat, this.lon_shift, ll_lon, ll_lat, ur_lon, ur_lat);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
/** A Lambert conformal conic grid with uniform grid spacing */
|
|
221
|
-
class LambertGrid extends Grid {
|
|
222
|
-
/**
|
|
223
|
-
* Create a Lambert conformal conic grid
|
|
224
|
-
* @param ni - The number of grid points in the i (longitude) direction
|
|
225
|
-
* @param nj - The number of grid points in the j (latitude) direction
|
|
226
|
-
* @param lon_0 - The standard longitude for the projection; this is also the center longitude for the projection
|
|
227
|
-
* @param lat_0 - The center latitude for the projection
|
|
228
|
-
* @param lat_std - The standard latitudes for the projection
|
|
229
|
-
* @param ll_x - The x coordinate in projection space of the lower-left corner of the grid
|
|
230
|
-
* @param ll_y - The y coordinate in projection space of the lower-left corner of the grid
|
|
231
|
-
* @param ur_x - The x coordinate in projection space of the upper-right corner of the grid
|
|
232
|
-
* @param ur_y - The y coordinate in projection space of the upper-right corner of the grid
|
|
233
|
-
*/
|
|
234
|
-
constructor(ni, nj, lon_0, lat_0, lat_std, ll_x, ll_y, ur_x, ur_y) {
|
|
235
|
-
super('lcc', true, ni, nj);
|
|
236
|
-
this.lon_0 = lon_0;
|
|
237
|
-
this.lat_0 = lat_0;
|
|
238
|
-
this.lat_std = lat_std;
|
|
239
|
-
this.ll_x = ll_x;
|
|
240
|
-
this.ll_y = ll_y;
|
|
241
|
-
this.ur_x = ur_x;
|
|
242
|
-
this.ur_y = ur_y;
|
|
243
|
-
this.lcc = lambertConformalConic({ lon_0: lon_0, lat_0: lat_0, lat_std: lat_std });
|
|
244
|
-
const dx = (this.ur_x - this.ll_x) / (this.ni - 1);
|
|
245
|
-
const dy = (this.ur_y - this.ll_y) / (this.nj - 1);
|
|
246
|
-
this.ll_cache = new Cache(() => {
|
|
247
|
-
const lons = new Float32Array(this.ni * this.nj);
|
|
248
|
-
const lats = new Float32Array(this.ni * this.nj);
|
|
249
|
-
for (let i = 0; i < this.ni; i++) {
|
|
250
|
-
const x = this.ll_x + i * dx;
|
|
251
|
-
for (let j = 0; j < this.nj; j++) {
|
|
252
|
-
const y = this.ll_y + j * dy;
|
|
253
|
-
const [lon, lat] = this.lcc(x, y, { inverse: true });
|
|
254
|
-
const idx = i + j * this.ni;
|
|
255
|
-
lons[idx] = lon;
|
|
256
|
-
lats[idx] = lat;
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
return { lons: lons, lats: lats };
|
|
260
|
-
});
|
|
261
|
-
this.gc_cache = new Cache(() => {
|
|
262
|
-
const x = new Float32Array(this.ni);
|
|
263
|
-
const y = new Float32Array(this.nj);
|
|
264
|
-
for (let i = 0; i < this.ni; i++) {
|
|
265
|
-
x[i] = this.ll_x + i * dx;
|
|
266
|
-
}
|
|
267
|
-
for (let j = 0; j < this.nj; j++) {
|
|
268
|
-
y[j] = this.ll_y + j * dy;
|
|
269
|
-
}
|
|
270
|
-
return { x: x, y: y };
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
copy(opts) {
|
|
274
|
-
opts = opts !== undefined ? opts : {};
|
|
275
|
-
const ni = opts.ni !== undefined ? opts.ni : this.ni;
|
|
276
|
-
const nj = opts.nj !== undefined ? opts.nj : this.nj;
|
|
277
|
-
const ll_x = opts.ll_x !== undefined ? opts.ll_x : this.ll_x;
|
|
278
|
-
const ll_y = opts.ll_y !== undefined ? opts.ll_y : this.ll_y;
|
|
279
|
-
const ur_x = opts.ur_x !== undefined ? opts.ur_x : this.ur_x;
|
|
280
|
-
const ur_y = opts.ur_y !== undefined ? opts.ur_y : this.ur_y;
|
|
281
|
-
return new LambertGrid(ni, nj, this.lon_0, this.lat_0, this.lat_std, ll_x, ll_y, ur_x, ur_y);
|
|
282
|
-
}
|
|
283
|
-
/**
|
|
284
|
-
* Get a list of longitudes and latitudes on the grid (internal method)
|
|
285
|
-
*/
|
|
286
|
-
getEarthCoords() {
|
|
287
|
-
return this.ll_cache.getValue();
|
|
288
|
-
}
|
|
289
|
-
getGridCoords() {
|
|
290
|
-
return this.gc_cache.getValue();
|
|
291
|
-
}
|
|
292
|
-
transform(x, y, opts) {
|
|
293
|
-
opts = opts === undefined ? {} : opts;
|
|
294
|
-
const inverse = 'inverse' in opts ? opts.inverse : false;
|
|
295
|
-
return this.lcc(x, y, { inverse: inverse });
|
|
296
|
-
}
|
|
297
|
-
getThinnedGrid(thin_x, thin_y) {
|
|
298
|
-
const dx = (this.ur_x - this.ll_x) / this.ni;
|
|
299
|
-
const dy = (this.ur_y - this.ll_y) / this.nj;
|
|
300
|
-
const ni = Math.ceil(this.ni / thin_x);
|
|
301
|
-
const nj = Math.ceil(this.nj / thin_y);
|
|
302
|
-
const ni_remove = (this.ni - 1) % thin_x;
|
|
303
|
-
const nj_remove = (this.nj - 1) % thin_y;
|
|
304
|
-
const ll_x = this.ll_x;
|
|
305
|
-
const ll_y = this.ll_y;
|
|
306
|
-
const ur_x = this.ur_x - ni_remove * dx;
|
|
307
|
-
const ur_y = this.ur_y - nj_remove * dy;
|
|
308
|
-
return new LambertGrid(ni, nj, this.lon_0, this.lat_0, this.lat_std, ll_x, ll_y, ur_x, ur_y);
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
4
|
function getArrayConstructor(ary) {
|
|
312
5
|
return ary.constructor;
|
|
313
6
|
}
|
|
@@ -324,34 +17,25 @@ class RawScalarField {
|
|
|
324
17
|
if (grid.ni * grid.nj != data.length) {
|
|
325
18
|
throw `Data size (${data.length}) doesn't match the grid dimensions (${grid.ni} x ${grid.nj}; expected ${grid.ni * grid.nj} points)`;
|
|
326
19
|
}
|
|
20
|
+
this.contour_cache = new Cache(async (opts) => {
|
|
21
|
+
return await contourCreator(this.data, this.grid, opts);
|
|
22
|
+
});
|
|
327
23
|
}
|
|
328
24
|
/** @internal */
|
|
329
25
|
getTextureData() {
|
|
330
26
|
// Need to give float16 data as uint16s to make WebGL happy: https://github.com/petamoriken/float16/issues/105
|
|
27
|
+
const raw_data = this.data;
|
|
331
28
|
let data;
|
|
332
|
-
if (
|
|
333
|
-
data =
|
|
29
|
+
if (raw_data instanceof Float32Array) {
|
|
30
|
+
data = raw_data;
|
|
334
31
|
}
|
|
335
32
|
else {
|
|
336
|
-
data = new Uint16Array(
|
|
33
|
+
data = new Uint16Array(raw_data.buffer);
|
|
337
34
|
}
|
|
338
35
|
return data;
|
|
339
36
|
}
|
|
340
|
-
|
|
341
|
-
return
|
|
342
|
-
}
|
|
343
|
-
getThinnedField(thin_x, thin_y) {
|
|
344
|
-
const arrayType = getArrayConstructor(this.data);
|
|
345
|
-
const new_grid = this.grid.getThinnedGrid(thin_x, thin_y);
|
|
346
|
-
const new_data = new arrayType(new_grid.ni * new_grid.nj);
|
|
347
|
-
for (let i = 0; i < new_grid.ni; i++) {
|
|
348
|
-
for (let j = 0; j < new_grid.nj; j++) {
|
|
349
|
-
const idx_old = i * thin_x + this.grid.ni * j * thin_y;
|
|
350
|
-
const idx = i + new_grid.ni * j;
|
|
351
|
-
new_data[idx] = this.data[idx_old];
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
return new RawScalarField(new_grid, new_data);
|
|
37
|
+
async getContours(opts) {
|
|
38
|
+
return await this.contour_cache.getValue(opts);
|
|
355
39
|
}
|
|
356
40
|
/**
|
|
357
41
|
* Create a new field by aggregating a number of fields using a specific function
|
|
@@ -385,70 +69,39 @@ class RawVectorField {
|
|
|
385
69
|
*/
|
|
386
70
|
constructor(grid, u, v, opts) {
|
|
387
71
|
opts = opts === undefined ? {} : opts;
|
|
388
|
-
const arrayType = getArrayConstructor(u);
|
|
389
72
|
this.u = new RawScalarField(grid, u);
|
|
390
73
|
this.v = new RawScalarField(grid, v);
|
|
391
74
|
this.relative_to = opts.relative_to === undefined ? 'grid' : opts.relative_to;
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
const u = this.u.data[icd];
|
|
401
|
-
const v = this.v.data[icd];
|
|
402
|
-
if (Math.abs(u) < 1e-6 && Math.abs(v) < 1e-6) {
|
|
403
|
-
u_rot[icd] = 0;
|
|
404
|
-
v_rot[icd] = 0;
|
|
405
|
-
continue;
|
|
406
|
-
}
|
|
407
|
-
const [x, y] = grid.transform(lon, lat);
|
|
408
|
-
const [x_pertlon, y_pertlon] = grid.transform(lon + 0.01, lat);
|
|
409
|
-
const mag_pertlon = Math.hypot(x - x_pertlon, y - y_pertlon);
|
|
410
|
-
const x_dotlon = (x_pertlon - x) / mag_pertlon;
|
|
411
|
-
const y_dotlon = (y_pertlon - y) / mag_pertlon;
|
|
412
|
-
let x_dotlat, y_dotlat;
|
|
413
|
-
if (grid.is_conformal) {
|
|
414
|
-
// If the grid is conformal, v and u are rotated by the same amount in the same direction.
|
|
415
|
-
x_dotlat = -y_dotlon;
|
|
416
|
-
y_dotlat = x_dotlon;
|
|
417
|
-
}
|
|
418
|
-
else {
|
|
419
|
-
// If the grid is non-conformal, we need a fully general change of basis from grid coordinates to earth coordinates.
|
|
420
|
-
const [x_pertlat, y_pertlat] = grid.transform(lon, lat + 0.01);
|
|
421
|
-
const mag_pertlat = Math.hypot(x - x_pertlat, y - y_pertlat);
|
|
422
|
-
x_dotlat = (x_pertlat - x) / mag_pertlat;
|
|
423
|
-
y_dotlat = (y_pertlat - y) / mag_pertlat;
|
|
424
|
-
}
|
|
425
|
-
u_rot[icd] = x_dotlon * u + y_dotlon * v;
|
|
426
|
-
v_rot[icd] = x_dotlat * u + y_dotlat * v;
|
|
427
|
-
}
|
|
428
|
-
return { u: new RawScalarField(grid, u_rot), v: new RawScalarField(grid, v_rot) };
|
|
429
|
-
});
|
|
75
|
+
}
|
|
76
|
+
getTextureData() {
|
|
77
|
+
// Need to give float16 data as uint16s to make WebGL happy: https://github.com/petamoriken/float16/issues/105
|
|
78
|
+
const raw_u = this.u.data;
|
|
79
|
+
const raw_v = this.v.data;
|
|
80
|
+
const u = raw_u instanceof Float32Array ? raw_u : new Uint16Array(raw_u.buffer);
|
|
81
|
+
const v = raw_v instanceof Float32Array ? raw_v : new Uint16Array(raw_v.buffer);
|
|
82
|
+
return { u: u, v: v };
|
|
430
83
|
}
|
|
431
84
|
getThinnedField(thin_x, thin_y) {
|
|
432
|
-
const
|
|
433
|
-
const
|
|
434
|
-
|
|
85
|
+
const new_grid = this.grid.getThinnedGrid(thin_x, thin_y);
|
|
86
|
+
const thinGrid = (data) => {
|
|
87
|
+
const arrayType = getArrayConstructor(data);
|
|
88
|
+
const new_data = new arrayType(new_grid.ni * new_grid.nj);
|
|
89
|
+
for (let i = 0; i < new_grid.ni; i++) {
|
|
90
|
+
for (let j = 0; j < new_grid.nj; j++) {
|
|
91
|
+
const idx_old = i * thin_x + this.grid.ni * j * thin_y;
|
|
92
|
+
const idx = i + new_grid.ni * j;
|
|
93
|
+
new_data[idx] = data[idx_old];
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return new_data;
|
|
97
|
+
};
|
|
98
|
+
const thin_u = thinGrid(this.u.data);
|
|
99
|
+
const thin_v = thinGrid(this.v.data);
|
|
100
|
+
return new RawVectorField(new_grid, thin_u, thin_v, { relative_to: this.relative_to });
|
|
435
101
|
}
|
|
436
102
|
get grid() {
|
|
437
103
|
return this.u.grid;
|
|
438
104
|
}
|
|
439
|
-
toEarthRelative() {
|
|
440
|
-
let u, v;
|
|
441
|
-
if (this.relative_to == 'earth') {
|
|
442
|
-
u = this.u;
|
|
443
|
-
v = this.v;
|
|
444
|
-
}
|
|
445
|
-
else {
|
|
446
|
-
const { u: u_, v: v_ } = this.rotate_cache.getValue();
|
|
447
|
-
u = u_;
|
|
448
|
-
v = v_;
|
|
449
|
-
}
|
|
450
|
-
return new RawVectorField(u.grid, u.data, v.data, { relative_to: 'earth' });
|
|
451
|
-
}
|
|
452
105
|
}
|
|
453
106
|
/** A class grid of wind profiles */
|
|
454
107
|
class RawProfileField {
|
|
@@ -463,9 +116,10 @@ class RawProfileField {
|
|
|
463
116
|
}
|
|
464
117
|
/** Get the gridded storm motion vector field (internal method) */
|
|
465
118
|
getStormMotionGrid() {
|
|
119
|
+
const profiles = this.profiles;
|
|
466
120
|
const u = new Float16Array(this.grid.ni * this.grid.nj).fill(parseFloat('nan'));
|
|
467
121
|
const v = new Float16Array(this.grid.ni * this.grid.nj).fill(parseFloat('nan'));
|
|
468
|
-
|
|
122
|
+
profiles.forEach(prof => {
|
|
469
123
|
const idx = prof.ilon + this.grid.ni * prof.jlat;
|
|
470
124
|
u[idx] = prof.smu;
|
|
471
125
|
v[idx] = prof.smv;
|
|
@@ -473,4 +127,4 @@ class RawProfileField {
|
|
|
473
127
|
return new RawVectorField(this.grid, u, v, { relative_to: 'grid' });
|
|
474
128
|
}
|
|
475
129
|
}
|
|
476
|
-
export { RawScalarField, RawVectorField, RawProfileField
|
|
130
|
+
export { RawScalarField, RawVectorField, RawProfileField };
|