autumnplot-gl 4.0.0-beta → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -207
- package/dist/812.autumnplot-gl.js +2 -0
- package/dist/812.autumnplot-gl.js.map +1 -0
- 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 +38 -5
- package/lib/AutumnTypes.js +7 -1
- package/lib/Barbs.d.ts +12 -2
- package/lib/Barbs.js +9 -0
- package/lib/BillboardCollection.d.ts +2 -2
- package/lib/BillboardCollection.js +14 -14
- package/lib/Color.d.ts +1 -0
- package/lib/Color.js +1 -0
- package/lib/ColorBar.d.ts +14 -0
- package/lib/ColorBar.js +15 -8
- package/lib/Colormap.d.ts +9 -1
- package/lib/Colormap.js +24 -1
- package/lib/Contour.d.ts +26 -1
- package/lib/Contour.js +24 -2
- package/lib/ContourCreator.worker.d.ts +25 -0
- package/lib/{ContourCreator.js → ContourCreator.worker.js} +15 -14
- package/lib/Fill.d.ts +31 -11
- package/lib/Fill.js +38 -18
- package/lib/Hodographs.d.ts +19 -3
- package/lib/Hodographs.js +45 -20
- package/lib/Map.d.ts +13 -1
- package/lib/Map.js +62 -8
- package/lib/Paintball.d.ts +14 -5
- package/lib/Paintball.js +96 -46
- package/lib/PlotComponent.d.ts +9 -3
- package/lib/PlotComponent.js +36 -1
- package/lib/PlotLayer.d.ts +2 -2
- package/lib/PlotLayer.js +2 -2
- package/lib/PlotLayer.worker.js +9 -3
- package/lib/RawField.d.ts +223 -27
- package/lib/RawField.js +413 -59
- package/lib/StationPlot.d.ts +78 -11
- package/lib/StationPlot.js +113 -30
- package/lib/TextCollection.d.ts +5 -0
- package/lib/TextCollection.js +82 -9
- package/lib/WasmInterface.d.ts +7 -0
- package/lib/WasmInterface.js +11 -0
- package/lib/WorkerPool.d.ts +8 -0
- package/lib/WorkerPool.js +77 -0
- package/lib/cpp/marchingsquares.js +127 -13
- package/lib/cpp/marchingsquares.wasm +0 -0
- package/lib/cpp/marchingsquares_embind.d.ts +16 -3
- package/lib/grids/AutoZoom.d.ts +21 -0
- package/lib/grids/AutoZoom.js +63 -0
- package/lib/grids/DomainBuffer.d.ts +14 -0
- package/lib/grids/DomainBuffer.js +16 -0
- package/lib/grids/Geostationary.d.ts +35 -0
- package/lib/grids/Geostationary.js +47 -0
- package/lib/grids/Grid.d.ts +36 -0
- package/lib/grids/Grid.js +12 -0
- package/lib/grids/GridCoordinates.d.ts +10 -0
- package/lib/grids/GridCoordinates.js +64 -0
- package/lib/grids/LambertGrid.d.ts +73 -0
- package/lib/grids/LambertGrid.js +92 -0
- package/lib/grids/PlateCarreeGrid.d.ts +46 -0
- package/lib/grids/PlateCarreeGrid.js +55 -0
- package/lib/grids/PlateCarreeRotatedGrid.d.ts +53 -0
- package/lib/grids/PlateCarreeRotatedGrid.js +65 -0
- package/lib/grids/RadarSweepGrid.d.ts +46 -0
- package/lib/grids/RadarSweepGrid.js +74 -0
- package/lib/grids/StructuredGrid.d.ts +49 -0
- package/lib/grids/StructuredGrid.js +103 -0
- package/lib/grids/UnstructuredGrid.d.ts +56 -0
- package/lib/grids/UnstructuredGrid.js +102 -0
- package/lib/index.d.ts +23 -6
- package/lib/index.js +18 -8
- package/lib/utils.d.ts +11 -2
- package/lib/utils.js +63 -1
- package/package.json +4 -3
- package/dist/110.autumnplot-gl.js +0 -2
- package/dist/110.autumnplot-gl.js.map +0 -1
- package/lib/ContourCreator.d.ts +0 -22
- package/lib/Grid.d.ts +0 -263
- package/lib/Grid.js +0 -547
- package/lib/ParticleTracer.d.ts +0 -19
- package/lib/ParticleTracer.js +0 -37
package/lib/Grid.js
DELETED
|
@@ -1,547 +0,0 @@
|
|
|
1
|
-
import { Float16Array } from "@petamoriken/float16";
|
|
2
|
-
import { WGLBuffer, WGLTexture } from "autumn-wgl";
|
|
3
|
-
import { LngLat, lambertConformalConic, rotateSphere } from "./Map";
|
|
4
|
-
import { getGLFormatTypeAlignment, layer_worker } from "./PlotComponent";
|
|
5
|
-
import { Cache, getArrayConstructor, getMinZoom } from "./utils";
|
|
6
|
-
import { kdTree } from "kd-tree-javascript";
|
|
7
|
-
function argMin(ary) {
|
|
8
|
-
if (ary.length === 0) {
|
|
9
|
-
return -1;
|
|
10
|
-
}
|
|
11
|
-
let min = ary[0];
|
|
12
|
-
let minIndex = 0;
|
|
13
|
-
for (let i = 1; i < ary.length; i++) {
|
|
14
|
-
if (ary[i] < min) {
|
|
15
|
-
minIndex = i;
|
|
16
|
-
min = ary[i];
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
return minIndex;
|
|
20
|
-
}
|
|
21
|
-
async function makeWGLDomainBuffers(gl, grid, simplify_ni, simplify_nj) {
|
|
22
|
-
const texcoord_margin_r = 1 / (2 * grid.ni);
|
|
23
|
-
const texcoord_margin_s = 1 / (2 * grid.nj);
|
|
24
|
-
const { lats: field_lats, lons: field_lons } = grid.getEarthCoords(simplify_ni, simplify_nj);
|
|
25
|
-
const domain_coords = await layer_worker.makeDomainVerticesAndTexCoords(field_lats, field_lons, simplify_ni, simplify_nj, texcoord_margin_r, texcoord_margin_s);
|
|
26
|
-
const vertices = new WGLBuffer(gl, domain_coords['vertices'], 2, gl.TRIANGLE_STRIP);
|
|
27
|
-
const texcoords = new WGLBuffer(gl, domain_coords['tex_coords'], 2, gl.TRIANGLE_STRIP);
|
|
28
|
-
return { 'vertices': vertices, 'texcoords': texcoords };
|
|
29
|
-
}
|
|
30
|
-
async function makeWGLBillboardBuffers(gl, grid, thin_fac, map_max_zoom) {
|
|
31
|
-
const { lats: field_lats, lons: field_lons } = grid.getEarthCoords();
|
|
32
|
-
const min_zoom = grid.getMinVisibleZoom(thin_fac);
|
|
33
|
-
const bb_elements = await layer_worker.makeBBElements(field_lats, field_lons, min_zoom, grid.ni, grid.nj, map_max_zoom);
|
|
34
|
-
const vertices = new WGLBuffer(gl, bb_elements['pts'], 2, gl.POINTS, { per_instance: true });
|
|
35
|
-
const texcoords = new WGLBuffer(gl, bb_elements['tex_coords'], 2, gl.POINTS, { per_instance: true });
|
|
36
|
-
return { 'vertices': vertices, 'texcoords': texcoords };
|
|
37
|
-
}
|
|
38
|
-
function makeVectorRotationTexture(gl, grid, data_are_earth_relative) {
|
|
39
|
-
const coords = grid.getEarthCoords();
|
|
40
|
-
const rot_vals = new Float16Array(grid.ni * grid.nj).fill(parseFloat('nan'));
|
|
41
|
-
if (data_are_earth_relative) {
|
|
42
|
-
rot_vals.fill(0);
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
if (!grid.is_conformal) {
|
|
46
|
-
// If the grid is non-conformal, we need a fully general change of basis from grid coordinates to earth coordinates. This is not supported for now, so warn about it.
|
|
47
|
-
console.warn('Vector rotations for non-conformal projections are not supported. The output may look incorrect.');
|
|
48
|
-
}
|
|
49
|
-
for (let icd = 0; icd < coords.lats.length; icd++) {
|
|
50
|
-
const lon = coords.lons[icd];
|
|
51
|
-
const lat = coords.lats[icd];
|
|
52
|
-
rot_vals[icd] = grid.getVectorRotationAtPoint(lon, lat);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
const { format, type, row_alignment } = getGLFormatTypeAlignment(gl, 'float16');
|
|
56
|
-
const rot_img = {
|
|
57
|
-
format: format, type: type, row_alignment: row_alignment, image: new Uint16Array(rot_vals.buffer),
|
|
58
|
-
width: grid.ni, height: grid.nj, mag_filter: gl.LINEAR
|
|
59
|
-
};
|
|
60
|
-
const rot_tex = new WGLTexture(gl, rot_img);
|
|
61
|
-
return { 'rotation': rot_tex };
|
|
62
|
-
}
|
|
63
|
-
class Grid {
|
|
64
|
-
constructor(type, is_conformal, ni, nj) {
|
|
65
|
-
this.type = type;
|
|
66
|
-
this.is_conformal = is_conformal;
|
|
67
|
-
this.ni = ni;
|
|
68
|
-
this.nj = nj;
|
|
69
|
-
this.billboard_buffer_cache = new Cache((gl, thin_fac, max_zoom) => {
|
|
70
|
-
return makeWGLBillboardBuffers(gl, this, thin_fac, max_zoom);
|
|
71
|
-
});
|
|
72
|
-
this.vector_rotation_cache = new Cache((gl, data_are_earth_relative) => {
|
|
73
|
-
return makeVectorRotationTexture(gl, this, data_are_earth_relative);
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
async getWGLBillboardBuffers(gl, thin_fac, max_zoom) {
|
|
77
|
-
return await this.billboard_buffer_cache.getValue(gl, thin_fac, max_zoom);
|
|
78
|
-
}
|
|
79
|
-
getVectorRotationAtPoint(lon, lat) {
|
|
80
|
-
const [x, y] = this.transform(lon, lat);
|
|
81
|
-
const [x_pertlon, y_pertlon] = this.transform(lon + 0.01, lat);
|
|
82
|
-
return Math.atan2(y_pertlon - y, x_pertlon - x);
|
|
83
|
-
}
|
|
84
|
-
getVectorRotationTexture(gl, data_are_earth_relative) {
|
|
85
|
-
return this.vector_rotation_cache.getValue(gl, data_are_earth_relative);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
/** A structured grid (in this case meaning a cartesian grid with i and j coordinates) */
|
|
89
|
-
class StructuredGrid extends Grid {
|
|
90
|
-
constructor(type, is_conformal, ni, nj, thin_x, thin_y) {
|
|
91
|
-
super(type, is_conformal, ni, nj);
|
|
92
|
-
this.thin_x = thin_x === undefined ? 1 : thin_x;
|
|
93
|
-
this.thin_y = thin_y === undefined ? 1 : thin_y;
|
|
94
|
-
this.buffer_cache = new Cache((gl) => {
|
|
95
|
-
const new_ni = Math.max(Math.floor(this.ni / 20), 20);
|
|
96
|
-
const new_nj = Math.max(Math.floor(this.nj / 20), 20);
|
|
97
|
-
return makeWGLDomainBuffers(gl, this, new_ni, new_nj);
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
/** @internal */
|
|
101
|
-
xyThinFromMaxZoom(thin_fac, map_max_zoom) {
|
|
102
|
-
const n_density_tiers = Math.log2(thin_fac);
|
|
103
|
-
const n_inaccessible_tiers = Math.max(n_density_tiers + 1 - map_max_zoom, 0);
|
|
104
|
-
const xy_thin = Math.pow(2, n_inaccessible_tiers);
|
|
105
|
-
return [xy_thin, xy_thin];
|
|
106
|
-
}
|
|
107
|
-
/** @internal */
|
|
108
|
-
getMinVisibleZoom(thin_fac) {
|
|
109
|
-
const min_zoom = new Uint8Array(this.ni * this.nj);
|
|
110
|
-
const zoom_thin_fac = thin_fac / Math.max(this.thin_x, this.thin_y);
|
|
111
|
-
for (let ilat = 0; ilat < this.nj * this.thin_y; ilat++) {
|
|
112
|
-
for (let ilon = 0; ilon < this.ni * this.thin_x; ilon++) {
|
|
113
|
-
const idx = ilat * this.ni + ilon;
|
|
114
|
-
min_zoom[idx] = getMinZoom(ilat, ilon, zoom_thin_fac);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
return min_zoom;
|
|
118
|
-
}
|
|
119
|
-
/** @internal */
|
|
120
|
-
thinDataArray(original_grid, ary) {
|
|
121
|
-
const arrayType = getArrayConstructor(ary);
|
|
122
|
-
const new_data = new arrayType(this.ni * this.nj);
|
|
123
|
-
for (let i = 0; i < this.ni; i++) {
|
|
124
|
-
for (let j = 0; j < this.nj; j++) {
|
|
125
|
-
const idx_old = i * this.thin_x + original_grid.ni * j * this.thin_y;
|
|
126
|
-
const idx = i + this.ni * j;
|
|
127
|
-
new_data[idx] = ary[idx_old];
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
return new_data;
|
|
131
|
-
}
|
|
132
|
-
async getWGLBuffers(gl) {
|
|
133
|
-
return await this.buffer_cache.getValue(gl);
|
|
134
|
-
}
|
|
135
|
-
sampleNearestGridPoint(lon, lat, ary) {
|
|
136
|
-
const [x, y] = this.transform(lon, lat);
|
|
137
|
-
const { x: xs, y: ys } = this.getGridCoords();
|
|
138
|
-
const ll_x = xs[0];
|
|
139
|
-
const ur_x = xs[xs.length - 1];
|
|
140
|
-
const dx = xs[1] - xs[0];
|
|
141
|
-
const ll_y = ys[0];
|
|
142
|
-
const ur_y = ys[ys.length - 1];
|
|
143
|
-
const dy = ys[1] - ys[0];
|
|
144
|
-
if (x < ll_x - 0.5 * dx || x > ur_x + 0.5 * dx || y < ll_y - 0.5 * dy || y > ur_y + 0.5 * dy) {
|
|
145
|
-
return { sample: NaN, sample_lon: NaN, sample_lat: NaN };
|
|
146
|
-
}
|
|
147
|
-
const i_min = argMin(xs.map(xv => Math.abs(xv - x)));
|
|
148
|
-
const j_min = argMin(ys.map(yv => Math.abs(yv - y)));
|
|
149
|
-
const idx = i_min + j_min * this.ni;
|
|
150
|
-
const [lon_min, lat_min] = this.transform(xs[i_min], ys[j_min], { inverse: true });
|
|
151
|
-
return { sample: ary[idx], sample_lon: lon_min, sample_lat: lat_min };
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
/** A plate carree (a.k.a. lat/lon) grid with uniform grid spacing */
|
|
155
|
-
class PlateCarreeGrid extends StructuredGrid {
|
|
156
|
-
/**
|
|
157
|
-
* Create a plate carree grid
|
|
158
|
-
* @param ni - The number of grid points in the i (longitude) direction
|
|
159
|
-
* @param nj - The number of grid points in the j (latitude) direction
|
|
160
|
-
* @param ll_lon - The longitude of the lower left corner of the grid
|
|
161
|
-
* @param ll_lat - The latitude of the lower left corner of the grid
|
|
162
|
-
* @param ur_lon - The longitude of the upper right corner of the grid
|
|
163
|
-
* @param ur_lat - The latitude of the upper right corner of the grid
|
|
164
|
-
*/
|
|
165
|
-
constructor(ni, nj, ll_lon, ll_lat, ur_lon, ur_lat, thin_x, thin_y) {
|
|
166
|
-
super('latlon', true, ni, nj, thin_x, thin_y);
|
|
167
|
-
this.ll_lon = ll_lon;
|
|
168
|
-
this.ll_lat = ll_lat;
|
|
169
|
-
this.ur_lon = ur_lon;
|
|
170
|
-
this.ur_lat = ur_lat;
|
|
171
|
-
const dlon = (this.ur_lon - this.ll_lon) / (this.ni - 1);
|
|
172
|
-
const dlat = (this.ur_lat - this.ll_lat) / (this.nj - 1);
|
|
173
|
-
this.ll_cache = new Cache((ni, nj) => {
|
|
174
|
-
const lons = new Float32Array(ni * nj);
|
|
175
|
-
const lats = new Float32Array(ni * nj);
|
|
176
|
-
const dlon_req = (this.ni - 1) / (ni - 1) * dlon;
|
|
177
|
-
const dlat_req = (this.nj - 1) / (nj - 1) * dlat;
|
|
178
|
-
for (let i = 0; i < ni; i++) {
|
|
179
|
-
for (let j = 0; j < nj; j++) {
|
|
180
|
-
const idx = i + j * ni;
|
|
181
|
-
lons[idx] = this.ll_lon + i * dlon_req;
|
|
182
|
-
lats[idx] = this.ll_lat + j * dlat_req;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
return { 'lons': lons, 'lats': lats };
|
|
186
|
-
});
|
|
187
|
-
this.gc_cache = new Cache(() => {
|
|
188
|
-
const x = new Float32Array(this.ni);
|
|
189
|
-
const y = new Float32Array(this.nj);
|
|
190
|
-
for (let i = 0; i < this.ni; i++) {
|
|
191
|
-
x[i] = this.ll_lon + i * dlon;
|
|
192
|
-
}
|
|
193
|
-
for (let j = 0; j < this.nj; j++) {
|
|
194
|
-
y[j] = this.ll_lat + j * dlat;
|
|
195
|
-
}
|
|
196
|
-
return { x: x, y: y };
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
/** @internal */
|
|
200
|
-
copy(opts) {
|
|
201
|
-
opts = opts !== undefined ? opts : {};
|
|
202
|
-
const ni = opts.ni !== undefined ? opts.ni : this.ni;
|
|
203
|
-
const nj = opts.nj !== undefined ? opts.nj : this.nj;
|
|
204
|
-
const ll_lon = opts.ll_lon !== undefined ? opts.ll_lon : this.ll_lon;
|
|
205
|
-
const ll_lat = opts.ll_lat !== undefined ? opts.ll_lat : this.ll_lat;
|
|
206
|
-
const ur_lon = opts.ur_lon !== undefined ? opts.ur_lon : this.ur_lon;
|
|
207
|
-
const ur_lat = opts.ur_lat !== undefined ? opts.ur_lat : this.ur_lat;
|
|
208
|
-
return new PlateCarreeGrid(ni, nj, ll_lon, ll_lat, ur_lon, ur_lat);
|
|
209
|
-
}
|
|
210
|
-
/**
|
|
211
|
-
* @internal
|
|
212
|
-
* Get a list of longitudes and latitudes on the grid (internal method)
|
|
213
|
-
*/
|
|
214
|
-
getEarthCoords(ni, nj) {
|
|
215
|
-
ni = ni === undefined ? this.ni : ni;
|
|
216
|
-
nj = nj === undefined ? this.nj : nj;
|
|
217
|
-
return this.ll_cache.getValue(ni, nj);
|
|
218
|
-
}
|
|
219
|
-
/** @internal */
|
|
220
|
-
getGridCoords() {
|
|
221
|
-
return this.gc_cache.getValue();
|
|
222
|
-
}
|
|
223
|
-
/** @internal */
|
|
224
|
-
transform(x, y, opts) {
|
|
225
|
-
return [x, y];
|
|
226
|
-
}
|
|
227
|
-
/** @internal */
|
|
228
|
-
getThinnedGrid(thin_fac, map_max_zoom) {
|
|
229
|
-
const [thin_x, thin_y] = this.xyThinFromMaxZoom(thin_fac, map_max_zoom);
|
|
230
|
-
const dlon = (this.ur_lon - this.ll_lon) / this.ni;
|
|
231
|
-
const dlat = (this.ur_lat - this.ll_lat) / this.nj;
|
|
232
|
-
const ni = Math.ceil(this.ni / thin_x);
|
|
233
|
-
const nj = Math.ceil(this.nj / thin_y);
|
|
234
|
-
const ni_remove = (this.ni - 1) % thin_x;
|
|
235
|
-
const nj_remove = (this.nj - 1) % thin_y;
|
|
236
|
-
const ll_lon = this.ll_lon;
|
|
237
|
-
const ll_lat = this.ll_lat;
|
|
238
|
-
const ur_lon = this.ur_lon - ni_remove * dlon;
|
|
239
|
-
const ur_lat = this.ur_lat - nj_remove * dlat;
|
|
240
|
-
return new PlateCarreeGrid(ni, nj, ll_lon, ll_lat, ur_lon, ur_lat, this.thin_x * thin_x, this.thin_y * thin_y);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
/** A rotated lat-lon (plate carree) grid with uniform grid spacing */
|
|
244
|
-
class PlateCarreeRotatedGrid extends StructuredGrid {
|
|
245
|
-
/**
|
|
246
|
-
* Create a Lambert conformal conic grid
|
|
247
|
-
* @param ni - The number of grid points in the i (longitude) direction
|
|
248
|
-
* @param nj - The number of grid points in the j (latitude) direction
|
|
249
|
-
* @param np_lon - The longitude of the north pole for the rotated grid
|
|
250
|
-
* @param np_lat - The latitude of the north pole for the rotated grid
|
|
251
|
-
* @param lon_shift - The angle around the rotated north pole to shift the central meridian
|
|
252
|
-
* @param ll_lon - The longitude of the lower left corner of the grid (on the rotated earth)
|
|
253
|
-
* @param ll_lat - The latitude of the lower left corner of the grid (on the rotated earth)
|
|
254
|
-
* @param ur_lon - The longitude of the upper right corner of the grid (on the rotated earth)
|
|
255
|
-
* @param ur_lat - The latitude of the upper right corner of the grid (on the rotated earth)
|
|
256
|
-
*/
|
|
257
|
-
constructor(ni, nj, np_lon, np_lat, lon_shift, ll_lon, ll_lat, ur_lon, ur_lat, thin_x, thin_y) {
|
|
258
|
-
super('latlonrot', true, ni, nj, thin_x, thin_y);
|
|
259
|
-
this.np_lon = np_lon;
|
|
260
|
-
this.np_lat = np_lat;
|
|
261
|
-
this.lon_shift = lon_shift;
|
|
262
|
-
this.ll_lon = ll_lon;
|
|
263
|
-
this.ll_lat = ll_lat;
|
|
264
|
-
this.ur_lon = ur_lon;
|
|
265
|
-
this.ur_lat = ur_lat;
|
|
266
|
-
this.llrot = rotateSphere({ np_lon: np_lon, np_lat: np_lat, lon_shift: lon_shift });
|
|
267
|
-
const dlon = (this.ur_lon - this.ll_lon) / (this.ni - 1);
|
|
268
|
-
const dlat = (this.ur_lat - this.ll_lat) / (this.nj - 1);
|
|
269
|
-
this.ll_cache = new Cache((ni, nj) => {
|
|
270
|
-
const lons = new Float32Array(ni * nj);
|
|
271
|
-
const lats = new Float32Array(ni * nj);
|
|
272
|
-
const dlon_req = (this.ni - 1) / (ni - 1) * dlon;
|
|
273
|
-
const dlat_req = (this.nj - 1) / (nj - 1) * dlat;
|
|
274
|
-
for (let i = 0; i < ni; i++) {
|
|
275
|
-
const lon_p = this.ll_lon + i * dlon_req;
|
|
276
|
-
for (let j = 0; j < nj; j++) {
|
|
277
|
-
const lat_p = this.ll_lat + j * dlat_req;
|
|
278
|
-
const [lon, lat] = this.llrot(lon_p, lat_p);
|
|
279
|
-
const idx = i + j * ni;
|
|
280
|
-
lons[idx] = lon;
|
|
281
|
-
lats[idx] = lat;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
return { lons: lons, lats: lats };
|
|
285
|
-
});
|
|
286
|
-
this.gc_cache = new Cache(() => {
|
|
287
|
-
const x = new Float32Array(this.ni);
|
|
288
|
-
const y = new Float32Array(this.nj);
|
|
289
|
-
for (let i = 0; i < this.ni; i++) {
|
|
290
|
-
x[i] = this.ll_lon + i * dlon;
|
|
291
|
-
}
|
|
292
|
-
for (let j = 0; j < this.nj; j++) {
|
|
293
|
-
y[j] = this.ll_lat + j * dlat;
|
|
294
|
-
}
|
|
295
|
-
return { x: x, y: y };
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
/** @internal */
|
|
299
|
-
copy(opts) {
|
|
300
|
-
opts = opts !== undefined ? opts : {};
|
|
301
|
-
const ni = opts.ni !== undefined ? opts.ni : this.ni;
|
|
302
|
-
const nj = opts.nj !== undefined ? opts.nj : this.nj;
|
|
303
|
-
const ll_lon = opts.ll_lon !== undefined ? opts.ll_lon : this.ll_lon;
|
|
304
|
-
const ll_lat = opts.ll_lat !== undefined ? opts.ll_lat : this.ll_lat;
|
|
305
|
-
const ur_lon = opts.ur_lon !== undefined ? opts.ur_lon : this.ur_lon;
|
|
306
|
-
const ur_lat = opts.ur_lat !== undefined ? opts.ur_lat : this.ur_lat;
|
|
307
|
-
return new PlateCarreeRotatedGrid(ni, nj, this.np_lon, this.np_lat, this.lon_shift, ll_lon, ll_lat, ur_lon, ur_lat);
|
|
308
|
-
}
|
|
309
|
-
/**
|
|
310
|
-
* @internal
|
|
311
|
-
* Get a list of longitudes and latitudes on the grid
|
|
312
|
-
*/
|
|
313
|
-
getEarthCoords(ni, nj) {
|
|
314
|
-
ni = ni === undefined ? this.ni : ni;
|
|
315
|
-
nj = nj === undefined ? this.nj : nj;
|
|
316
|
-
return this.ll_cache.getValue(ni, nj);
|
|
317
|
-
}
|
|
318
|
-
/** @internal */
|
|
319
|
-
getGridCoords() {
|
|
320
|
-
return this.gc_cache.getValue();
|
|
321
|
-
}
|
|
322
|
-
/** @internal */
|
|
323
|
-
transform(x, y, opts) {
|
|
324
|
-
opts = opts === undefined ? {} : opts;
|
|
325
|
-
const inverse = 'inverse' in opts ? opts.inverse : false;
|
|
326
|
-
return this.llrot(x, y, { inverse: !inverse });
|
|
327
|
-
}
|
|
328
|
-
/** @internal */
|
|
329
|
-
getThinnedGrid(thin_fac, map_max_zoom) {
|
|
330
|
-
const [thin_x, thin_y] = this.xyThinFromMaxZoom(thin_fac, map_max_zoom);
|
|
331
|
-
const dlon = (this.ur_lon - this.ll_lon) / this.ni;
|
|
332
|
-
const dlat = (this.ur_lat - this.ll_lat) / this.nj;
|
|
333
|
-
const ni = Math.ceil(this.ni / thin_x);
|
|
334
|
-
const nj = Math.ceil(this.nj / thin_y);
|
|
335
|
-
const ni_remove = (this.ni - 1) % thin_x;
|
|
336
|
-
const nj_remove = (this.nj - 1) % thin_y;
|
|
337
|
-
const ll_lon = this.ll_lon;
|
|
338
|
-
const ll_lat = this.ll_lat;
|
|
339
|
-
const ur_lon = this.ur_lon - ni_remove * dlon;
|
|
340
|
-
const ur_lat = this.ur_lat - nj_remove * dlat;
|
|
341
|
-
return new PlateCarreeRotatedGrid(ni, nj, this.np_lon, this.np_lat, this.lon_shift, ll_lon, ll_lat, ur_lon, ur_lat, this.thin_x * thin_x, this.thin_y * thin_y);
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
/** A Lambert conformal conic grid with uniform grid spacing */
|
|
345
|
-
class LambertGrid extends StructuredGrid {
|
|
346
|
-
/**
|
|
347
|
-
* Create a Lambert conformal conic grid from the lower-left and upper-right corner x/y values.
|
|
348
|
-
* @param ni - The number of grid points in the i (longitude) direction
|
|
349
|
-
* @param nj - The number of grid points in the j (latitude) direction
|
|
350
|
-
* @param lon_0 - The standard longitude for the projection; this is also the center longitude for the projection
|
|
351
|
-
* @param lat_0 - The center latitude for the projection
|
|
352
|
-
* @param lat_std - The standard latitudes for the projection
|
|
353
|
-
* @param ll_x - The x coordinate in projection space of the lower-left corner of the grid
|
|
354
|
-
* @param ll_y - The y coordinate in projection space of the lower-left corner of the grid
|
|
355
|
-
* @param ur_x - The x coordinate in projection space of the upper-right corner of the grid
|
|
356
|
-
* @param ur_y - The y coordinate in projection space of the upper-right corner of the grid
|
|
357
|
-
*/
|
|
358
|
-
constructor(ni, nj, lon_0, lat_0, lat_std, ll_x, ll_y, ur_x, ur_y, thin_x, thin_y) {
|
|
359
|
-
super('lcc', true, ni, nj, thin_x, thin_y);
|
|
360
|
-
this.lon_0 = lon_0;
|
|
361
|
-
this.lat_0 = lat_0;
|
|
362
|
-
this.lat_std = lat_std;
|
|
363
|
-
this.ll_x = ll_x;
|
|
364
|
-
this.ll_y = ll_y;
|
|
365
|
-
this.ur_x = ur_x;
|
|
366
|
-
this.ur_y = ur_y;
|
|
367
|
-
this.lcc = lambertConformalConic({ lon_0: lon_0, lat_0: lat_0, lat_std: lat_std });
|
|
368
|
-
const dx = (this.ur_x - this.ll_x) / this.ni;
|
|
369
|
-
const dy = (this.ur_y - this.ll_y) / this.nj;
|
|
370
|
-
this.ll_cache = new Cache((ni, nj) => {
|
|
371
|
-
const lons = new Float32Array(ni * nj);
|
|
372
|
-
const lats = new Float32Array(ni * nj);
|
|
373
|
-
const dx_req = (this.ni - 1) / (ni - 1) * dx;
|
|
374
|
-
const dy_req = (this.nj - 1) / (nj - 1) * dy;
|
|
375
|
-
for (let i = 0; i < ni; i++) {
|
|
376
|
-
const x = this.ll_x + i * dx_req;
|
|
377
|
-
for (let j = 0; j < nj; j++) {
|
|
378
|
-
const y = this.ll_y + j * dy_req;
|
|
379
|
-
const [lon, lat] = this.lcc(x, y, { inverse: true });
|
|
380
|
-
const idx = i + j * ni;
|
|
381
|
-
lons[idx] = lon;
|
|
382
|
-
lats[idx] = lat;
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
return { lons: lons, lats: lats };
|
|
386
|
-
});
|
|
387
|
-
this.gc_cache = new Cache(() => {
|
|
388
|
-
const x = new Float32Array(this.ni);
|
|
389
|
-
const y = new Float32Array(this.nj);
|
|
390
|
-
for (let i = 0; i < this.ni; i++) {
|
|
391
|
-
x[i] = this.ll_x + i * dx;
|
|
392
|
-
}
|
|
393
|
-
for (let j = 0; j < this.nj; j++) {
|
|
394
|
-
y[j] = this.ll_y + j * dy;
|
|
395
|
-
}
|
|
396
|
-
return { x: x, y: y };
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
/**
|
|
400
|
-
* Create a Lambert conformal conic grid from the lower-left grid point coordinate and a dx and dy.
|
|
401
|
-
* @param ni - The number of grid points in the i (longitude) direction
|
|
402
|
-
* @param nj - The number of grid points in the j (latitude) direction
|
|
403
|
-
* @param lon_0 - The standard longitude for the projection; this is also the center longitude for the projection
|
|
404
|
-
* @param lat_0 - The center latitude for the projection
|
|
405
|
-
* @param lat_std - The standard latitudes for the projection
|
|
406
|
-
* @param ll_lon - The longitude of the lower-left corner of the grid
|
|
407
|
-
* @param ll_lat - The latitude of the lower-left corner of the grid
|
|
408
|
-
* @param dx - The grid dx in meters
|
|
409
|
-
* @param dy - The grid dy in meters
|
|
410
|
-
* @returns
|
|
411
|
-
*/
|
|
412
|
-
static fromLLCornerLonLat(ni, nj, lon_0, lat_0, lat_std, ll_lon, ll_lat, dx, dy) {
|
|
413
|
-
const lcc = lambertConformalConic({ lon_0: lon_0, lat_0: lat_0, lat_std: lat_std });
|
|
414
|
-
const [ll_x, ll_y] = lcc(ll_lon, ll_lat);
|
|
415
|
-
return new LambertGrid(ni, nj, lon_0, lat_0, lat_std, ll_x, ll_y, ll_x + ni * dx, ll_y + nj * dy);
|
|
416
|
-
}
|
|
417
|
-
/** @internal */
|
|
418
|
-
copy(opts) {
|
|
419
|
-
opts = opts !== undefined ? opts : {};
|
|
420
|
-
const ni = opts.ni !== undefined ? opts.ni : this.ni;
|
|
421
|
-
const nj = opts.nj !== undefined ? opts.nj : this.nj;
|
|
422
|
-
const ll_x = opts.ll_x !== undefined ? opts.ll_x : this.ll_x;
|
|
423
|
-
const ll_y = opts.ll_y !== undefined ? opts.ll_y : this.ll_y;
|
|
424
|
-
const ur_x = opts.ur_x !== undefined ? opts.ur_x : this.ur_x;
|
|
425
|
-
const ur_y = opts.ur_y !== undefined ? opts.ur_y : this.ur_y;
|
|
426
|
-
return new LambertGrid(ni, nj, this.lon_0, this.lat_0, this.lat_std, ll_x, ll_y, ur_x, ur_y);
|
|
427
|
-
}
|
|
428
|
-
/**
|
|
429
|
-
* @internal
|
|
430
|
-
* Get a list of longitudes and latitudes on the grid
|
|
431
|
-
*/
|
|
432
|
-
getEarthCoords(ni, nj) {
|
|
433
|
-
ni = ni === undefined ? this.ni : ni;
|
|
434
|
-
nj = nj === undefined ? this.nj : nj;
|
|
435
|
-
return this.ll_cache.getValue(ni, nj);
|
|
436
|
-
}
|
|
437
|
-
/** @internal */
|
|
438
|
-
getGridCoords() {
|
|
439
|
-
return this.gc_cache.getValue();
|
|
440
|
-
}
|
|
441
|
-
/** @internal */
|
|
442
|
-
transform(x, y, opts) {
|
|
443
|
-
opts = opts === undefined ? {} : opts;
|
|
444
|
-
const inverse = opts.inverse === undefined ? false : opts.inverse;
|
|
445
|
-
return this.lcc(x, y, { inverse: inverse });
|
|
446
|
-
}
|
|
447
|
-
/** @internal */
|
|
448
|
-
getThinnedGrid(thin_fac, map_max_zoom) {
|
|
449
|
-
const [thin_x, thin_y] = this.xyThinFromMaxZoom(thin_fac, map_max_zoom);
|
|
450
|
-
const dx = (this.ur_x - this.ll_x) / this.ni;
|
|
451
|
-
const dy = (this.ur_y - this.ll_y) / this.nj;
|
|
452
|
-
const ni = Math.ceil(this.ni / thin_x);
|
|
453
|
-
const nj = Math.ceil(this.nj / thin_y);
|
|
454
|
-
const ni_remove = (this.ni - 1) % thin_x;
|
|
455
|
-
const nj_remove = (this.nj - 1) % thin_y;
|
|
456
|
-
const ll_x = this.ll_x;
|
|
457
|
-
const ll_y = this.ll_y;
|
|
458
|
-
const ur_x = this.ur_x - ni_remove * dx;
|
|
459
|
-
const ur_y = this.ur_y - nj_remove * dy;
|
|
460
|
-
return new LambertGrid(ni, nj, this.lon_0, this.lat_0, this.lat_std, ll_x, ll_y, ur_x, ur_y, this.thin_x * thin_x, this.thin_y * thin_y);
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
/** An unstructured grid */
|
|
464
|
-
class UnstructuredGrid extends Grid {
|
|
465
|
-
/**
|
|
466
|
-
* Create an unstructured grid
|
|
467
|
-
* @param coords - The lat/lon coordinates of the grid points
|
|
468
|
-
*/
|
|
469
|
-
constructor(coords, zoom) {
|
|
470
|
-
const MAX_DIM = 4096;
|
|
471
|
-
super('unstructured', true, Math.min(coords.length, MAX_DIM), Math.floor(coords.length / MAX_DIM) + 1);
|
|
472
|
-
this.coords = coords;
|
|
473
|
-
this.zoom_arg = zoom === undefined ? null : zoom;
|
|
474
|
-
this.zoom_cache = new Cache((thin_fac) => {
|
|
475
|
-
const MAP_MAX_ZOOM = 24;
|
|
476
|
-
const offset = Math.log2(thin_fac);
|
|
477
|
-
const kd_nodes = this.coords.map(c => ({ ...new LngLat(c.lon, c.lat).toMercatorCoord(), min_zoom: MAP_MAX_ZOOM }));
|
|
478
|
-
const tree = new kdTree([...kd_nodes], (a, b) => Math.max(Math.abs(a.x - b.x), Math.abs(a.y - b.y)), ['x', 'y']);
|
|
479
|
-
const recursiveThin = (x, y, depth) => {
|
|
480
|
-
const size = Math.pow(0.5, depth + 1);
|
|
481
|
-
const nodes = tree.nearest({ x: x, y: y, min_zoom: 0 }, 2, size);
|
|
482
|
-
if (nodes.length > 0) {
|
|
483
|
-
const [node, dist] = nodes.sort((a, b) => a[1] - b[1])[0];
|
|
484
|
-
if (node.min_zoom == MAP_MAX_ZOOM) {
|
|
485
|
-
node.min_zoom = Math.max(depth - offset, 0);
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
if (nodes.length > 1 && depth < MAP_MAX_ZOOM + offset) {
|
|
489
|
-
recursiveThin(x - size / 2, y - size / 2, depth + 1);
|
|
490
|
-
recursiveThin(x + size / 2, y - size / 2, depth + 1);
|
|
491
|
-
recursiveThin(x - size / 2, y + size / 2, depth + 1);
|
|
492
|
-
recursiveThin(x + size / 2, y + size / 2, depth + 1);
|
|
493
|
-
}
|
|
494
|
-
};
|
|
495
|
-
recursiveThin(0.5, 0.5, 0);
|
|
496
|
-
return new Uint8Array(kd_nodes.map(n => n.min_zoom));
|
|
497
|
-
});
|
|
498
|
-
}
|
|
499
|
-
/** @internal */
|
|
500
|
-
copy() {
|
|
501
|
-
return new UnstructuredGrid(this.coords);
|
|
502
|
-
}
|
|
503
|
-
/** @internal */
|
|
504
|
-
getEarthCoords() {
|
|
505
|
-
return { lons: new Float32Array(this.coords.map(c => c.lon)), lats: new Float32Array(this.coords.map(c => c.lat)) };
|
|
506
|
-
}
|
|
507
|
-
/** @internal */
|
|
508
|
-
getGridCoords() {
|
|
509
|
-
const { lons, lats } = this.getEarthCoords();
|
|
510
|
-
return { x: lons, y: lats };
|
|
511
|
-
}
|
|
512
|
-
/** @internal */
|
|
513
|
-
transform(x, y, opts) {
|
|
514
|
-
return [x, y];
|
|
515
|
-
}
|
|
516
|
-
/** @internal */
|
|
517
|
-
getMinVisibleZoom(thin_fac) {
|
|
518
|
-
if (this.zoom_arg !== null)
|
|
519
|
-
return this.zoom_arg;
|
|
520
|
-
return this.zoom_cache.getValue(thin_fac);
|
|
521
|
-
}
|
|
522
|
-
/** @internal */
|
|
523
|
-
getThinnedGrid(thin_fac, map_max_zoom) {
|
|
524
|
-
const min_zoom = this.getMinVisibleZoom(thin_fac);
|
|
525
|
-
return new UnstructuredGrid(this.coords.filter((ll, ill) => min_zoom[ill] <= map_max_zoom), min_zoom.filter(ll => ll <= map_max_zoom));
|
|
526
|
-
}
|
|
527
|
-
/** @internal */
|
|
528
|
-
thinDataArray(original_grid, ary) {
|
|
529
|
-
let i_new = 0;
|
|
530
|
-
const arrayType = getArrayConstructor(ary);
|
|
531
|
-
const new_data = new arrayType(this.ni * this.nj);
|
|
532
|
-
for (let i = 0; i < original_grid.coords.length; i++) {
|
|
533
|
-
if (this.coords[i_new].lat == original_grid.coords[i].lat && this.coords[i_new].lon == original_grid.coords[i].lon) {
|
|
534
|
-
new_data[i_new++] = ary[i];
|
|
535
|
-
if (i_new >= this.coords.length)
|
|
536
|
-
break;
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
return new_data;
|
|
540
|
-
}
|
|
541
|
-
sampleNearestGridPoint(lon, lat, ary) {
|
|
542
|
-
// TAS: This is gonna be slow. Need to think about using the kdTree here.
|
|
543
|
-
const idx = argMin(this.coords.map(c => (c.lon - lon) * (c.lon - lon) + (c.lat - lat) * (c.lat - lat)));
|
|
544
|
-
return { sample: ary[idx], sample_lon: this.coords[idx].lon, sample_lat: this.coords[idx].lat };
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
export { Grid, StructuredGrid, PlateCarreeGrid, PlateCarreeRotatedGrid, LambertGrid, UnstructuredGrid };
|
package/lib/ParticleTracer.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
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 };
|
package/lib/ParticleTracer.js
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
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 };
|