@aguacerowx/mapsgl 0.0.31 → 0.0.41
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/index.js +34 -2
- package/package.json +13 -3
- package/src/GridRenderLayer.js +105 -86
- package/src/MapManager.js +47 -15
- package/src/NexradSitesOverlay.js +148 -0
- package/src/NexradWeatherController.js +491 -0
- package/src/NwsWatchesWarningsOverlay.js +768 -0
- package/src/SatelliteShaderManager.js +999 -0
- package/src/WeatherLayerManager.js +800 -110
- package/src/WorkerPool.js +340 -0
- package/src/nexrad/MapboxRadarLayer.bundled.js +810 -0
- package/src/nexrad/MapboxRadarLayer.ts +784 -0
- package/src/nexrad/PreprocessedSweepParser.ts +226 -0
- package/src/nexrad/buildRadarRayGeometry.ts +97 -0
- package/src/nexrad/level3StormRelative.ts +116 -0
- package/src/nexrad/loadNexradSites.ts +41 -0
- package/src/nexrad/nexradArchiveCache.ts +64 -0
- package/src/nexrad/nexradCrossSectionSampleAtLatLon.ts +121 -0
- package/src/nexrad/nexradLevel3Products.ts +549 -0
- package/src/nexrad/nexradMapboxFrameOpts.js +106 -0
- package/src/nexrad/radarArchiveCore.bundled.js +4206 -0
- package/src/nexrad/radarArchiveCore.bundled.js.map +7 -0
- package/src/nexrad/radarArchiveCore.ts +1737 -0
- package/src/nexrad/radarDecode.worker.bundled.js +809 -0
- package/src/nexrad/radarDecode.worker.ts +227 -0
- package/src/nexrad/radarFrameGpuMatch.ts +111 -0
- package/src/nwsAlertsSupport.js +860 -0
- package/src/nwsEventColorsDefaults.js +133 -0
- package/src/nwsSdkConstants.js +360 -0
- package/src/nwsWarningCustomizationKey.gen.js +496 -0
- package/src/satelliteDefaultColormaps.js +37 -0
- package/src/satelliteKtxWorker.js +225 -0
- package/src/satelliteShader.js +17 -0
package/index.js
CHANGED
|
@@ -3,10 +3,42 @@
|
|
|
3
3
|
// Import the specific classes you want to make public from this package's internal files.
|
|
4
4
|
import { WeatherLayerManager } from './src/WeatherLayerManager.js';
|
|
5
5
|
import { MapManager } from './src/MapManager.js';
|
|
6
|
+
import { NwsWatchesWarningsOverlay, NWS_DEFAULT_LINE_BEFORE_LAYER_ID } from './src/NwsWatchesWarningsOverlay.js';
|
|
7
|
+
import {
|
|
8
|
+
collectNwsExpressionEventKeys,
|
|
9
|
+
NWS_ALERT_EVENT_NAMES,
|
|
10
|
+
NWS_EVENT_COLORS,
|
|
11
|
+
NWS_SDK_EVENT_COLOR,
|
|
12
|
+
NWS_SDK_EVENT_NAMES,
|
|
13
|
+
} from './src/nwsSdkConstants.js';
|
|
14
|
+
import {
|
|
15
|
+
filterNwsAlertsByIncludedList,
|
|
16
|
+
filterNwsAlertsByScope,
|
|
17
|
+
filterToConvectiveSdkEvents,
|
|
18
|
+
isNwsEventIncludedInAllowlist,
|
|
19
|
+
SDK_ALLOWED_EVENT_SET,
|
|
20
|
+
} from './src/nwsAlertsSupport.js';
|
|
6
21
|
|
|
7
22
|
// Now, export them both so your test app can import them.
|
|
8
23
|
// This is the "public face" of your @aguacerowx/mapsgl package.
|
|
9
24
|
export {
|
|
10
25
|
WeatherLayerManager,
|
|
11
|
-
MapManager
|
|
12
|
-
|
|
26
|
+
MapManager,
|
|
27
|
+
NwsWatchesWarningsOverlay,
|
|
28
|
+
NWS_DEFAULT_LINE_BEFORE_LAYER_ID,
|
|
29
|
+
collectNwsExpressionEventKeys,
|
|
30
|
+
NWS_ALERT_EVENT_NAMES,
|
|
31
|
+
NWS_EVENT_COLORS,
|
|
32
|
+
NWS_SDK_EVENT_COLOR,
|
|
33
|
+
NWS_SDK_EVENT_NAMES,
|
|
34
|
+
filterNwsAlertsByIncludedList,
|
|
35
|
+
filterNwsAlertsByScope,
|
|
36
|
+
filterToConvectiveSdkEvents,
|
|
37
|
+
isNwsEventIncludedInAllowlist,
|
|
38
|
+
SDK_ALLOWED_EVENT_SET,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export {
|
|
42
|
+
DEFAULT_SATELLITE_LONGWAVE_IR_COLORMAP,
|
|
43
|
+
DEFAULT_SATELLITE_WATER_VAPOR_COLORMAP,
|
|
44
|
+
} from './src/satelliteDefaultColormaps.js';
|
package/package.json
CHANGED
|
@@ -1,19 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aguacerowx/mapsgl",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.41",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
8
8
|
"main": "index.js",
|
|
9
9
|
"type": "module",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"bundle-nexrad": "esbuild src/nexrad/radarDecode.worker.ts --bundle --format=esm --platform=browser --outfile=src/nexrad/radarDecode.worker.bundled.js && esbuild src/nexrad/radarArchiveCore.ts --bundle --format=esm --platform=browser --outfile=src/nexrad/radarArchiveCore.bundled.js --external:@aguacerowx/javascript-sdk && esbuild src/nexrad/MapboxRadarLayer.ts --bundle --format=esm --platform=browser --outfile=src/nexrad/MapboxRadarLayer.bundled.js --external:mapbox-gl --external:@aguacerowx/javascript-sdk",
|
|
12
|
+
"gen:nws-key": "esbuild ../../../aguacero-frontend/src/components/WarningsMenu/nwsWarningCustomizationKey.ts --bundle --format=esm --platform=neutral --outfile=src/nwsWarningCustomizationKey.gen.js"
|
|
13
|
+
},
|
|
10
14
|
"files": [
|
|
11
15
|
"index.js",
|
|
12
16
|
"src"
|
|
13
17
|
],
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"esbuild": "^0.21.5"
|
|
20
|
+
},
|
|
14
21
|
"dependencies": {
|
|
15
|
-
"@aguacerowx/javascript-sdk": "^0.0.
|
|
22
|
+
"@aguacerowx/javascript-sdk": "^0.0.20",
|
|
23
|
+
"buffer": "^6.0.3",
|
|
24
|
+
"fzstd": "^0.1.1",
|
|
16
25
|
"mapbox-gl": "^3.4.0",
|
|
17
|
-
"proj4": "^2.11.0"
|
|
26
|
+
"proj4": "^2.11.0",
|
|
27
|
+
"seek-bzip": "^2.0.0"
|
|
18
28
|
}
|
|
19
29
|
}
|
package/src/GridRenderLayer.js
CHANGED
|
@@ -2,6 +2,11 @@ import proj4 from 'proj4';
|
|
|
2
2
|
|
|
3
3
|
const MERCATOR_SAFE_LIMIT = 89;
|
|
4
4
|
|
|
5
|
+
/** Mapbox GL 3+ uses a WebGL2 context; LUMINANCE is not a valid sized internal format in WebGL2. */
|
|
6
|
+
function isWebGL2Context(gl) {
|
|
7
|
+
return typeof WebGL2RenderingContext !== 'undefined' && gl instanceof WebGL2RenderingContext;
|
|
8
|
+
}
|
|
9
|
+
|
|
5
10
|
function hrdpsObliqueTransform(rotated_lon, rotated_lat) {
|
|
6
11
|
const o_lat_p = 53.91148;
|
|
7
12
|
const o_lon_p = 245.305142;
|
|
@@ -27,7 +32,18 @@ function hrdpsObliqueTransform(rotated_lon, rotated_lat) {
|
|
|
27
32
|
}
|
|
28
33
|
|
|
29
34
|
export class GridRenderLayer {
|
|
30
|
-
|
|
35
|
+
/**
|
|
36
|
+
* @param {string | { layerId?: string, id?: string }} idOrOpts - Mapbox custom layer id, or `{ layerId }` / `{ id }` (RN / prop passthrough parity).
|
|
37
|
+
*/
|
|
38
|
+
constructor(idOrOpts) {
|
|
39
|
+
let id;
|
|
40
|
+
if (typeof idOrOpts === 'string') {
|
|
41
|
+
id = idOrOpts;
|
|
42
|
+
} else if (idOrOpts != null && typeof idOrOpts === 'object') {
|
|
43
|
+
id = idOrOpts.layerId ?? idOrOpts.id;
|
|
44
|
+
} else {
|
|
45
|
+
id = idOrOpts;
|
|
46
|
+
}
|
|
31
47
|
this.id = id;
|
|
32
48
|
this.type = 'custom';
|
|
33
49
|
this.renderingMode = '2d';
|
|
@@ -110,16 +126,16 @@ export class GridRenderLayer {
|
|
|
110
126
|
try {
|
|
111
127
|
return this.map.transform.MercatorCoordinate.fromLngLat({ lon, lat });
|
|
112
128
|
} catch (e) {
|
|
113
|
-
|
|
129
|
+
// fall through to manual projection
|
|
114
130
|
}
|
|
115
131
|
}
|
|
116
|
-
|
|
132
|
+
|
|
117
133
|
// Fallback to imported mapboxgl MercatorCoordinate
|
|
118
134
|
if (mapboxgl && mapboxgl.MercatorCoordinate) {
|
|
119
135
|
try {
|
|
120
136
|
return mapboxgl.MercatorCoordinate.fromLngLat([lon, lat]);
|
|
121
137
|
} catch (e) {
|
|
122
|
-
|
|
138
|
+
// fall through to manual projection
|
|
123
139
|
}
|
|
124
140
|
}
|
|
125
141
|
|
|
@@ -404,12 +420,13 @@ export class GridRenderLayer {
|
|
|
404
420
|
raw_value = dequantize_val(total_value / total_weight);
|
|
405
421
|
}
|
|
406
422
|
}
|
|
407
|
-
|
|
408
|
-
if (raw_value < u_data_range.x) {
|
|
409
|
-
discard;
|
|
410
|
-
}
|
|
411
423
|
|
|
412
424
|
float converted_value = convert_units(raw_value);
|
|
425
|
+
|
|
426
|
+
// Compare against u_data_range in the same unit space as the colormap (after unit conversion).
|
|
427
|
+
if (converted_value < u_data_range.x) {
|
|
428
|
+
discard;
|
|
429
|
+
}
|
|
413
430
|
|
|
414
431
|
float colormap_coord = clamp((converted_value - u_data_range.x) / (u_data_range.y - u_data_range.x), 0.0, 1.0);
|
|
415
432
|
|
|
@@ -457,6 +474,39 @@ export class GridRenderLayer {
|
|
|
457
474
|
}
|
|
458
475
|
}
|
|
459
476
|
|
|
477
|
+
/**
|
|
478
|
+
* Upload quantized grid bytes (Uint8, centered at 128) to the active 2D texture.
|
|
479
|
+
* WebGL2: R8 + RED + UNSIGNED_BYTE (LUMINANCE is invalid). WebGL1: LUMINANCE.
|
|
480
|
+
*/
|
|
481
|
+
_uploadQuantizedGridTexture(tex, nx, ny, dataArray) {
|
|
482
|
+
const gl = this.gl;
|
|
483
|
+
gl.bindTexture(gl.TEXTURE_2D, tex);
|
|
484
|
+
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
|
|
485
|
+
if (isWebGL2Context(gl)) {
|
|
486
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.R8, nx, ny, 0, gl.RED, gl.UNSIGNED_BYTE, dataArray);
|
|
487
|
+
} else {
|
|
488
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, nx, ny, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, dataArray);
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* After updateDataTexture, register the same GPU texture under timeKey so the slider can
|
|
494
|
+
* switch back without re-fetching (avoids duplicating VRAM).
|
|
495
|
+
*/
|
|
496
|
+
registerCurrentDataTextureAsPreloaded(timeKey) {
|
|
497
|
+
if (!this.gl || !this.dataTexture || this.encoding == null) {
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
if (!this.preloadedTextures) this.preloadedTextures = new Map();
|
|
501
|
+
if (this.preloadedTextures.has(timeKey)) return;
|
|
502
|
+
this.preloadedTextures.set(timeKey, {
|
|
503
|
+
tex: this.dataTexture,
|
|
504
|
+
encoding: this.encoding,
|
|
505
|
+
nx: this.textureWidth,
|
|
506
|
+
ny: this.textureHeight
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
|
|
460
510
|
// Call this during preload to store a pre-uploaded GPU texture
|
|
461
511
|
storePreloadedTexture(timeKey, data, encoding, nx, ny) {
|
|
462
512
|
if (!this.gl) return;
|
|
@@ -464,13 +514,12 @@ export class GridRenderLayer {
|
|
|
464
514
|
|
|
465
515
|
const tex = gl.createTexture();
|
|
466
516
|
gl.bindTexture(gl.TEXTURE_2D, tex);
|
|
467
|
-
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
|
|
468
517
|
|
|
469
518
|
let dataArray = data instanceof Uint8Array ? data
|
|
470
519
|
: data.buffer instanceof ArrayBuffer ? new Uint8Array(data.buffer, data.byteOffset, data.byteLength)
|
|
471
520
|
: new Uint8Array(data);
|
|
472
521
|
|
|
473
|
-
|
|
522
|
+
this._uploadQuantizedGridTexture(tex, nx, ny, dataArray);
|
|
474
523
|
|
|
475
524
|
const filter = this.noSmoothing ? gl.NEAREST : gl.LINEAR;
|
|
476
525
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, filter);
|
|
@@ -480,9 +529,11 @@ export class GridRenderLayer {
|
|
|
480
529
|
gl.bindTexture(gl.TEXTURE_2D, null);
|
|
481
530
|
|
|
482
531
|
if (!this.preloadedTextures) this.preloadedTextures = new Map();
|
|
483
|
-
// Free old texture for this key if it exists
|
|
484
532
|
if (this.preloadedTextures.has(timeKey)) {
|
|
485
|
-
|
|
533
|
+
const prev = this.preloadedTextures.get(timeKey);
|
|
534
|
+
if (prev.tex && prev.tex !== this.dataTexture) {
|
|
535
|
+
gl.deleteTexture(prev.tex);
|
|
536
|
+
}
|
|
486
537
|
}
|
|
487
538
|
this.preloadedTextures.set(timeKey, { tex, encoding, nx, ny });
|
|
488
539
|
}
|
|
@@ -499,15 +550,6 @@ export class GridRenderLayer {
|
|
|
499
550
|
return true;
|
|
500
551
|
}
|
|
501
552
|
|
|
502
|
-
// Call in onRemove() to clean up
|
|
503
|
-
clearPreloadedTextures() {
|
|
504
|
-
if (!this.gl || !this.preloadedTextures) return;
|
|
505
|
-
for (const { tex } of this.preloadedTextures.values()) {
|
|
506
|
-
this.gl.deleteTexture(tex);
|
|
507
|
-
}
|
|
508
|
-
this.preloadedTextures.clear();
|
|
509
|
-
}
|
|
510
|
-
|
|
511
553
|
updateGeometry(corners, gridDef) {
|
|
512
554
|
|
|
513
555
|
// Cache the geometry
|
|
@@ -523,8 +565,7 @@ export class GridRenderLayer {
|
|
|
523
565
|
corners.lon_bl, corners.lat_bl, corners.lon_br, corners.lat_br];
|
|
524
566
|
|
|
525
567
|
if (coordValues.some(coord => !isFinite(coord))) {
|
|
526
|
-
|
|
527
|
-
return;
|
|
568
|
+
return;
|
|
528
569
|
}
|
|
529
570
|
|
|
530
571
|
const gridType = gridDef.type;
|
|
@@ -669,17 +710,13 @@ export class GridRenderLayer {
|
|
|
669
710
|
adjustedCorners.lon_br, adjustedCorners.lat_br];
|
|
670
711
|
|
|
671
712
|
if (adjustedCoordValues.some(coord => !isFinite(coord))) {
|
|
672
|
-
|
|
673
|
-
return;
|
|
713
|
+
return;
|
|
674
714
|
}
|
|
675
715
|
|
|
676
716
|
// Additional validation: check if latitudes are in valid range
|
|
677
717
|
const latValues = [adjustedCorners.lat_tl, adjustedCorners.lat_tr, adjustedCorners.lat_bl, adjustedCorners.lat_br];
|
|
678
718
|
if (latValues.some(lat => lat < -90 || lat > 90)) {
|
|
679
|
-
|
|
680
|
-
console.error('[GridLayer] Original corners were:', corners);
|
|
681
|
-
console.error('[GridLayer] Grid definition:', gridDef);
|
|
682
|
-
return;
|
|
719
|
+
return;
|
|
683
720
|
}
|
|
684
721
|
|
|
685
722
|
// Calculate longitude span to handle wrapping
|
|
@@ -745,13 +782,7 @@ export class GridRenderLayer {
|
|
|
745
782
|
|
|
746
783
|
// Validate coordinates
|
|
747
784
|
if (!isFinite(lon) || !isFinite(lat)) {
|
|
748
|
-
|
|
749
|
-
console.error(`[GridLayer] Interpolation inputs:`, {
|
|
750
|
-
t_x, t_y,
|
|
751
|
-
corners: adjustedCorners,
|
|
752
|
-
gridType: isGFSType ? 'GFS' : (isECMWFType ? 'ECMWF' : (isIconModel ? 'ICON' : 'OTHER'))
|
|
753
|
-
});
|
|
754
|
-
continue;
|
|
785
|
+
continue;
|
|
755
786
|
}
|
|
756
787
|
|
|
757
788
|
// Normalize longitude to [-180, 180] range
|
|
@@ -762,8 +793,7 @@ export class GridRenderLayer {
|
|
|
762
793
|
const mercator = this.getMercatorCoordinate(normalizedLon, lat);
|
|
763
794
|
|
|
764
795
|
if (!isFinite(mercator.x) || !isFinite(mercator.y)) {
|
|
765
|
-
|
|
766
|
-
continue;
|
|
796
|
+
continue;
|
|
767
797
|
}
|
|
768
798
|
|
|
769
799
|
vertices.push(mercator.x, mercator.y, tex_u, tex_v);
|
|
@@ -834,8 +864,7 @@ export class GridRenderLayer {
|
|
|
834
864
|
const [lon, lat] = hrdpsObliqueTransform(rot_lon, rot_lat);
|
|
835
865
|
|
|
836
866
|
if (!isFinite(lon) || !isFinite(lat)) {
|
|
837
|
-
|
|
838
|
-
continue;
|
|
867
|
+
continue;
|
|
839
868
|
}
|
|
840
869
|
|
|
841
870
|
const clampedLat = Math.max(-MERCATOR_SAFE_LIMIT, Math.min(MERCATOR_SAFE_LIMIT, lat));
|
|
@@ -843,8 +872,7 @@ export class GridRenderLayer {
|
|
|
843
872
|
const mercator = this.getMercatorCoordinate(lon, clampedLat);
|
|
844
873
|
|
|
845
874
|
if (!isFinite(mercator.x) || !isFinite(mercator.y)) {
|
|
846
|
-
|
|
847
|
-
continue;
|
|
875
|
+
continue;
|
|
848
876
|
}
|
|
849
877
|
|
|
850
878
|
// Texture coordinates - corrected for HRDPS orientation
|
|
@@ -854,8 +882,7 @@ export class GridRenderLayer {
|
|
|
854
882
|
vertices.push(mercator.x, mercator.y, tex_u, tex_v);
|
|
855
883
|
|
|
856
884
|
} catch (error) {
|
|
857
|
-
|
|
858
|
-
continue;
|
|
885
|
+
continue;
|
|
859
886
|
}
|
|
860
887
|
}
|
|
861
888
|
}
|
|
@@ -941,8 +968,7 @@ export class GridRenderLayer {
|
|
|
941
968
|
}
|
|
942
969
|
|
|
943
970
|
if (lon_raw > 0) {
|
|
944
|
-
|
|
945
|
-
vertexGrid[row][col] = null;
|
|
971
|
+
vertexGrid[row][col] = null;
|
|
946
972
|
continue;
|
|
947
973
|
}
|
|
948
974
|
|
|
@@ -987,8 +1013,7 @@ export class GridRenderLayer {
|
|
|
987
1013
|
}
|
|
988
1014
|
|
|
989
1015
|
if (vertices.length === 0) {
|
|
990
|
-
|
|
991
|
-
return;
|
|
1016
|
+
return;
|
|
992
1017
|
}
|
|
993
1018
|
|
|
994
1019
|
// Second pass: generate indices (this logic remains the same)
|
|
@@ -1080,8 +1105,7 @@ export class GridRenderLayer {
|
|
|
1080
1105
|
const [lon, lat] = proj4(projectionString, wgs84, [proj_x, proj_y]);
|
|
1081
1106
|
|
|
1082
1107
|
if (lon > 0) {
|
|
1083
|
-
|
|
1084
|
-
vertexGrid[row][col] = null;
|
|
1108
|
+
vertexGrid[row][col] = null;
|
|
1085
1109
|
continue;
|
|
1086
1110
|
}
|
|
1087
1111
|
|
|
@@ -1114,16 +1138,14 @@ export class GridRenderLayer {
|
|
|
1114
1138
|
validVertexCount++;
|
|
1115
1139
|
|
|
1116
1140
|
} catch (error) {
|
|
1117
|
-
|
|
1118
|
-
vertexGrid[row][col] = null;
|
|
1141
|
+
vertexGrid[row][col] = null;
|
|
1119
1142
|
continue;
|
|
1120
1143
|
}
|
|
1121
1144
|
}
|
|
1122
1145
|
}
|
|
1123
1146
|
|
|
1124
1147
|
if (vertices.length === 0) {
|
|
1125
|
-
|
|
1126
|
-
return;
|
|
1148
|
+
return;
|
|
1127
1149
|
}
|
|
1128
1150
|
|
|
1129
1151
|
// Second pass: generate indices only for quads where all 4 vertices are valid
|
|
@@ -1153,8 +1175,7 @@ export class GridRenderLayer {
|
|
|
1153
1175
|
|
|
1154
1176
|
// Check for potential dateline issues and log them
|
|
1155
1177
|
if (validMaxLon - validMinLon > 180) {
|
|
1156
|
-
|
|
1157
|
-
}
|
|
1178
|
+
}
|
|
1158
1179
|
|
|
1159
1180
|
const vertexData = new Float32Array(vertices);
|
|
1160
1181
|
gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer);
|
|
@@ -1172,14 +1193,12 @@ export class GridRenderLayer {
|
|
|
1172
1193
|
this.vertexCount = vertices.length / 4;
|
|
1173
1194
|
|
|
1174
1195
|
} else {
|
|
1175
|
-
|
|
1176
|
-
return;
|
|
1196
|
+
return;
|
|
1177
1197
|
}
|
|
1178
1198
|
}
|
|
1179
1199
|
|
|
1180
1200
|
updateDataTexture(data, encoding, nx, ny) {
|
|
1181
1201
|
if (!this.gl || !data) {
|
|
1182
|
-
console.warn('❌ updateDataTexture early exit - missing gl or data');
|
|
1183
1202
|
return;
|
|
1184
1203
|
}
|
|
1185
1204
|
|
|
@@ -1201,12 +1220,6 @@ export class GridRenderLayer {
|
|
|
1201
1220
|
const expectedSize = nx * ny;
|
|
1202
1221
|
|
|
1203
1222
|
if (data.length === 0 || data.length < expectedSize) {
|
|
1204
|
-
console.warn('⚠️ Data array is empty or too small - NO TEXTURE WILL BE CREATED', {
|
|
1205
|
-
dataLength: data.length,
|
|
1206
|
-
expected: expectedSize,
|
|
1207
|
-
nx: nx,
|
|
1208
|
-
ny: ny
|
|
1209
|
-
});
|
|
1210
1223
|
return;
|
|
1211
1224
|
}
|
|
1212
1225
|
|
|
@@ -1224,15 +1237,7 @@ export class GridRenderLayer {
|
|
|
1224
1237
|
this.dataTexture = gl.createTexture();
|
|
1225
1238
|
gl.bindTexture(gl.TEXTURE_2D, this.dataTexture);
|
|
1226
1239
|
|
|
1227
|
-
|
|
1228
|
-
// FIX: Set pixel store alignment to 1 to handle widths not divisible by 4
|
|
1229
|
-
gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1);
|
|
1230
|
-
|
|
1231
|
-
gl.texImage2D(gl.TEXTURE_2D, 0, gl.LUMINANCE, nx, ny, 0, gl.LUMINANCE, gl.UNSIGNED_BYTE, dataArray);
|
|
1232
|
-
} catch (error) {
|
|
1233
|
-
console.error('❌ texImage2D failed:', error);
|
|
1234
|
-
throw error;
|
|
1235
|
-
}
|
|
1240
|
+
this._uploadQuantizedGridTexture(this.dataTexture, nx, ny, dataArray);
|
|
1236
1241
|
|
|
1237
1242
|
// Set texture parameters
|
|
1238
1243
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
@@ -1302,11 +1307,15 @@ export class GridRenderLayer {
|
|
|
1302
1307
|
}
|
|
1303
1308
|
|
|
1304
1309
|
render(gl, matrix) {
|
|
1305
|
-
if (!this.dataTexture) {
|
|
1306
|
-
|
|
1310
|
+
if (!this.dataTexture || !this.encoding) {
|
|
1311
|
+
return;
|
|
1312
|
+
}
|
|
1313
|
+
if (!this.colormapTexture) {
|
|
1314
|
+
return;
|
|
1315
|
+
}
|
|
1316
|
+
if (!this.program || !this.vertexBuffer || !this.indexBuffer) {
|
|
1307
1317
|
return;
|
|
1308
1318
|
}
|
|
1309
|
-
if (!this.program || !this.encoding || !this.vertexBuffer || !this.indexBuffer) return;
|
|
1310
1319
|
gl.useProgram(this.program);
|
|
1311
1320
|
|
|
1312
1321
|
if (this.u_is_ptype) {
|
|
@@ -1340,30 +1349,40 @@ export class GridRenderLayer {
|
|
|
1340
1349
|
|
|
1341
1350
|
onRemove() {
|
|
1342
1351
|
if (!this.gl) return;
|
|
1352
|
+
const gl = this.gl;
|
|
1343
1353
|
|
|
1344
1354
|
if (this.program) {
|
|
1345
|
-
|
|
1355
|
+
gl.deleteProgram(this.program);
|
|
1346
1356
|
this.program = null;
|
|
1347
1357
|
}
|
|
1348
1358
|
if (this.vertexBuffer) {
|
|
1349
|
-
|
|
1359
|
+
gl.deleteBuffer(this.vertexBuffer);
|
|
1350
1360
|
this.vertexBuffer = null;
|
|
1351
1361
|
}
|
|
1352
1362
|
if (this.indexBuffer) {
|
|
1353
|
-
|
|
1363
|
+
gl.deleteBuffer(this.indexBuffer);
|
|
1354
1364
|
this.indexBuffer = null;
|
|
1355
1365
|
}
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1366
|
+
|
|
1367
|
+
const gridTextures = new Set();
|
|
1368
|
+
if (this.dataTexture) gridTextures.add(this.dataTexture);
|
|
1369
|
+
if (this.preloadedTextures) {
|
|
1370
|
+
for (const { tex } of this.preloadedTextures.values()) {
|
|
1371
|
+
if (tex) gridTextures.add(tex);
|
|
1372
|
+
}
|
|
1373
|
+
this.preloadedTextures.clear();
|
|
1359
1374
|
}
|
|
1375
|
+
for (const tex of gridTextures) {
|
|
1376
|
+
gl.deleteTexture(tex);
|
|
1377
|
+
}
|
|
1378
|
+
this.dataTexture = null;
|
|
1379
|
+
|
|
1360
1380
|
if (this.colormapTexture) {
|
|
1361
|
-
|
|
1381
|
+
gl.deleteTexture(this.colormapTexture);
|
|
1362
1382
|
this.colormapTexture = null;
|
|
1363
1383
|
}
|
|
1364
|
-
|
|
1384
|
+
|
|
1365
1385
|
this.map = null;
|
|
1366
1386
|
this.gl = null;
|
|
1367
|
-
this.clearPreloadedTextures();
|
|
1368
1387
|
}
|
|
1369
1388
|
}
|
package/src/MapManager.js
CHANGED
|
@@ -41,13 +41,28 @@ function isObject(item) {
|
|
|
41
41
|
const BASE_STYLE_URL = 'mapbox://styles/aguacerowx/cmfvox8mq004u01qm5nlg7qkt';
|
|
42
42
|
|
|
43
43
|
export class MapManager extends EventEmitter {
|
|
44
|
+
/**
|
|
45
|
+
* @param {string} containerId - DOM element id for the Mapbox map container.
|
|
46
|
+
* @param {object} options
|
|
47
|
+
* @param {string} [options.accessToken] - Mapbox access token (required unless `mapboxToken` is set).
|
|
48
|
+
* @param {string} [options.mapboxToken] - Alias for `accessToken` (e.g. React Native parity).
|
|
49
|
+
* @param {object} [options.mapOptions] - Passed through to `mapboxgl.Map` (after defaults); `style` here is used unless a valid custom style + anchor pair is set.
|
|
50
|
+
* @param {object} [options.customStyles] - Per-theme style overrides merged into {@link THEME_CONFIGS}.
|
|
51
|
+
* @param {'light'|'dark'} [options.defaultTheme]
|
|
52
|
+
* @param {'light'|'dark'} [options.theme] - Alias for `defaultTheme`.
|
|
53
|
+
* @param {string} [options.styleUrl] - Custom Mapbox style URL (alias: `styleURL`).
|
|
54
|
+
* @param {string} [options.styleURL] - Same as `styleUrl`.
|
|
55
|
+
* @param {string} [options.weatherBeforeLayerId] - Style layer id to insert Aguacero layers below (alias: `belowID`).
|
|
56
|
+
* @param {string} [options.belowID] - Same as `weatherBeforeLayerId`. With `styleUrl`/`styleURL`, required for correct stacking; default Aguacero style uses `AML_-_terrain` in {@link WeatherLayerManager} when omitted.
|
|
57
|
+
*/
|
|
44
58
|
constructor(containerId, options = {}) {
|
|
45
59
|
super();
|
|
46
|
-
|
|
60
|
+
const accessToken = options.accessToken || options.mapboxToken;
|
|
61
|
+
if (!containerId || !accessToken) {
|
|
47
62
|
throw new Error('A container ID and a Mapbox access token are required.');
|
|
48
63
|
}
|
|
49
64
|
|
|
50
|
-
mapboxgl.accessToken =
|
|
65
|
+
mapboxgl.accessToken = accessToken;
|
|
51
66
|
|
|
52
67
|
// --- THE FIX IS HERE ---
|
|
53
68
|
|
|
@@ -57,15 +72,12 @@ export class MapManager extends EventEmitter {
|
|
|
57
72
|
|
|
58
73
|
// 2. If developer provides custom styles, merge them into the defaults
|
|
59
74
|
if (options.customStyles) {
|
|
60
|
-
console.log('[MapManager] Custom styles provided. Merging...');
|
|
61
75
|
if (options.customStyles.light) {
|
|
62
76
|
lightTheme = deepMerge(lightTheme, options.customStyles.light);
|
|
63
77
|
}
|
|
64
78
|
if (options.customStyles.dark) {
|
|
65
79
|
darkTheme = deepMerge(darkTheme, options.customStyles.dark);
|
|
66
80
|
}
|
|
67
|
-
// Log to confirm the merge was successful
|
|
68
|
-
console.log('[MapManager] Final merged dark theme:', darkTheme);
|
|
69
81
|
}
|
|
70
82
|
|
|
71
83
|
// 3. Store the final, potentially merged, themes
|
|
@@ -76,22 +88,47 @@ export class MapManager extends EventEmitter {
|
|
|
76
88
|
|
|
77
89
|
// --- END OF FIX ---
|
|
78
90
|
|
|
79
|
-
const defaultThemeName = options.defaultTheme || 'light';
|
|
91
|
+
const defaultThemeName = options.theme || options.defaultTheme || 'light';
|
|
80
92
|
this.currentCustomizations = this.themes[defaultThemeName];
|
|
81
93
|
this.currentThemeName = defaultThemeName;
|
|
82
94
|
|
|
83
95
|
this.weatherLayerManagers = new Map();
|
|
84
|
-
|
|
96
|
+
|
|
97
|
+
const mapOptions = options.mapOptions || {};
|
|
98
|
+
let initialStyle = mapOptions.style !== undefined ? mapOptions.style : BASE_STYLE_URL;
|
|
99
|
+
/** @type {string | null} */
|
|
100
|
+
let weatherBeforeLayerId = null;
|
|
101
|
+
|
|
102
|
+
const customStyleUrl = options.styleURL || options.styleUrl;
|
|
103
|
+
const customBeforeId = options.belowID || options.weatherBeforeLayerId;
|
|
104
|
+
if (customStyleUrl) {
|
|
105
|
+
if (customBeforeId && String(customBeforeId).trim()) {
|
|
106
|
+
initialStyle = customStyleUrl;
|
|
107
|
+
weatherBeforeLayerId = String(customBeforeId).trim();
|
|
108
|
+
} else {
|
|
109
|
+
console.warn(
|
|
110
|
+
'[MapManager] A custom style (`styleUrl` / `styleURL`) was provided without a non-empty `weatherBeforeLayerId` / `belowID`. ' +
|
|
111
|
+
'Weather layers need a style layer id to insert below; keeping the default Aguacero style.'
|
|
112
|
+
);
|
|
113
|
+
if (mapOptions.style === undefined) {
|
|
114
|
+
initialStyle = BASE_STYLE_URL;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
this.weatherBeforeLayerId = weatherBeforeLayerId;
|
|
120
|
+
|
|
85
121
|
this.map = new mapboxgl.Map({
|
|
86
122
|
container: containerId,
|
|
87
|
-
style: BASE_STYLE_URL,
|
|
88
123
|
center: [-98, 39],
|
|
89
124
|
zoom: 3.5,
|
|
90
|
-
...
|
|
125
|
+
...mapOptions,
|
|
126
|
+
style: initialStyle,
|
|
91
127
|
});
|
|
128
|
+
|
|
129
|
+
this.map.__aguaceroMapsgl = { weatherBeforeLayerId };
|
|
92
130
|
|
|
93
131
|
this.map.on('load', () => {
|
|
94
|
-
console.log("[MapManager] Map loaded. Applying initial theme:", defaultThemeName);
|
|
95
132
|
applyStyleCustomizations(this.map, this.currentCustomizations);
|
|
96
133
|
this.emit('style:applied', {
|
|
97
134
|
themeName: this.currentThemeName,
|
|
@@ -103,7 +140,6 @@ export class MapManager extends EventEmitter {
|
|
|
103
140
|
// The rest of the methods (setTheme, setLabelGroupVisibility, etc.) are correct and remain unchanged...
|
|
104
141
|
setTheme(themeName) {
|
|
105
142
|
if (!this.themes[themeName]) {
|
|
106
|
-
console.error(`[MapManager] Theme "${themeName}" does not exist.`);
|
|
107
143
|
return;
|
|
108
144
|
}
|
|
109
145
|
|
|
@@ -141,7 +177,6 @@ export class MapManager extends EventEmitter {
|
|
|
141
177
|
for (let i = 0; i < parts.length - 1; i++) {
|
|
142
178
|
current = current[parts[i]];
|
|
143
179
|
if (!current) {
|
|
144
|
-
console.error(`Invalid label group key: ${groupKey}`);
|
|
145
180
|
return;
|
|
146
181
|
}
|
|
147
182
|
}
|
|
@@ -152,9 +187,6 @@ export class MapManager extends EventEmitter {
|
|
|
152
187
|
|
|
153
188
|
if (layerId && this.map.getLayer(layerId)) {
|
|
154
189
|
this.map.setLayoutProperty(layerId, 'visibility', visible ? 'visible' : 'none');
|
|
155
|
-
console.log(`[MapManager] Set visibility for ${layerId} to ${visible}`);
|
|
156
|
-
} else {
|
|
157
|
-
console.warn(`[MapManager] Could not find layer for label group key: ${groupKey} (mapped to ${mapKey})`);
|
|
158
190
|
}
|
|
159
191
|
}
|
|
160
192
|
|