astro-viewer 2.0.0 → 3.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.
Files changed (104) hide show
  1. package/dist/astroviewer.cjs +19832 -16986
  2. package/dist/astroviewer.js +1 -1
  3. package/dist/astroviewer.min.js +1 -1
  4. package/lib-esm/AstroSphere.d.ts +31 -2
  5. package/lib-esm/AstroSphere.js +267 -16
  6. package/lib-esm/AstroViewer.d.ts +33 -2
  7. package/lib-esm/AstroViewer.js +93 -0
  8. package/lib-esm/Camera.js +24 -8
  9. package/lib-esm/Config.js +12 -0
  10. package/lib-esm/Global.js +12 -0
  11. package/lib-esm/index.d.ts +8 -1
  12. package/lib-esm/index.js +18 -1
  13. package/lib-esm/model/AbstractSkyEntity.d.ts +10 -0
  14. package/lib-esm/model/AbstractSkyEntity.js +12 -0
  15. package/lib-esm/model/AstroGL.js +12 -0
  16. package/lib-esm/model/ColorMaps.js +12 -0
  17. package/lib-esm/model/MetadataColumn.js +12 -0
  18. package/lib-esm/model/MetadataManager.js +12 -0
  19. package/lib-esm/model/Point.js +12 -0
  20. package/lib-esm/model/Point2D.js +12 -0
  21. package/lib-esm/model/Source.js +12 -0
  22. package/lib-esm/model/SphereFoV.d.ts +30 -0
  23. package/lib-esm/model/SphereFoV.js +195 -0
  24. package/lib-esm/model/catalogues/CatalogueGL.js +12 -0
  25. package/lib-esm/model/catalogues/CatalogueProps.js +13 -1
  26. package/lib-esm/model/earth/WMTSAdapter.d.ts +12 -0
  27. package/lib-esm/model/earth/WMTSAdapter.js +97 -0
  28. package/lib-esm/model/earth/XYZAnchestorTile.d.ts +17 -0
  29. package/lib-esm/model/earth/XYZAnchestorTile.js +87 -0
  30. package/lib-esm/model/earth/XYZConfig.d.ts +72 -0
  31. package/lib-esm/model/earth/XYZConfig.js +13 -0
  32. package/lib-esm/model/earth/XYZFoVHelper.d.ts +9 -0
  33. package/lib-esm/model/earth/XYZFoVHelper.js +101 -0
  34. package/lib-esm/model/earth/XYZMap.d.ts +31 -0
  35. package/lib-esm/model/earth/XYZMap.js +208 -0
  36. package/lib-esm/model/earth/XYZMapDescriptor.d.ts +29 -0
  37. package/lib-esm/model/earth/XYZMapDescriptor.js +77 -0
  38. package/lib-esm/model/earth/XYZMeshBuilder.d.ts +6 -0
  39. package/lib-esm/model/earth/XYZMeshBuilder.js +108 -0
  40. package/lib-esm/model/earth/XYZTile.d.ts +38 -0
  41. package/lib-esm/model/earth/XYZTile.js +167 -0
  42. package/lib-esm/model/earth/XYZTileBuffer.d.ts +37 -0
  43. package/lib-esm/model/earth/XYZTileBuffer.js +139 -0
  44. package/lib-esm/model/earth/XYZTileRequestScheduler.d.ts +25 -0
  45. package/lib-esm/model/earth/XYZTileRequestScheduler.js +172 -0
  46. package/lib-esm/model/earth/XYZTypes.d.ts +24 -0
  47. package/lib-esm/model/earth/XYZTypes.js +13 -0
  48. package/lib-esm/model/earth/XYZVisibleTilesManager.d.ts +19 -0
  49. package/lib-esm/model/earth/XYZVisibleTilesManager.js +77 -0
  50. package/lib-esm/model/footprints/Footprint.js +12 -0
  51. package/lib-esm/model/footprints/FootprintProps.js +13 -1
  52. package/lib-esm/model/footprints/FootprintSetGL.js +13 -1
  53. package/lib-esm/model/grid/EquatorialGrid.js +20 -6
  54. package/lib-esm/model/grid/GridTextHelper.d.ts +10 -19
  55. package/lib-esm/model/grid/GridTextHelper.js +70 -67
  56. package/lib-esm/model/grid/HealpixGrid.d.ts +4 -4
  57. package/lib-esm/model/grid/HealpixGrid.js +19 -6
  58. package/lib-esm/model/grid/LonLatGrid.d.ts +40 -0
  59. package/lib-esm/model/grid/LonLatGrid.js +258 -0
  60. package/lib-esm/model/hips/AllSky.js +12 -0
  61. package/lib-esm/model/hips/AncestorTile.js +12 -0
  62. package/lib-esm/model/hips/FoVHelper.js +12 -0
  63. package/lib-esm/model/hips/HiPS.js +12 -9
  64. package/lib-esm/model/hips/HiPSDescriptor.js +12 -0
  65. package/lib-esm/model/hips/Tile.js +12 -0
  66. package/lib-esm/model/hips/TileBuffer.js +12 -0
  67. package/lib-esm/model/hips/VisibleTilesManager.js +12 -0
  68. package/lib-esm/model/tap/TapMetadata.js +12 -0
  69. package/lib-esm/model/tap/TapMetadataList.js +12 -0
  70. package/lib-esm/model/tap/TapRepo.js +12 -0
  71. package/lib-esm/model/terra/TerraFootprintSetGL.d.ts +4 -0
  72. package/lib-esm/model/terra/TerraFootprintSetGL.js +16 -0
  73. package/lib-esm/model/terra/TerraPointSetGL.d.ts +4 -0
  74. package/lib-esm/model/terra/TerraPointSetGL.js +16 -0
  75. package/lib-esm/services/SesameService.js +11 -3
  76. package/lib-esm/services/helpers.js +12 -0
  77. package/lib-esm/services/hipsNodeService.js +12 -0
  78. package/lib-esm/services/parse-hipslist.js +12 -0
  79. package/lib-esm/services/queryCatalogueByFoV.js +13 -1
  80. package/lib-esm/services/queryFootprintSetByFov.js +13 -1
  81. package/lib-esm/services/tapRepoService.js +13 -1
  82. package/lib-esm/services/types.js +12 -0
  83. package/lib-esm/shader/CatalogueShaderProgram.js +12 -0
  84. package/lib-esm/shader/FootprintShaderProgram.js +12 -0
  85. package/lib-esm/shader/GridShaderManager.js +12 -0
  86. package/lib-esm/shader/HiPSShaderProgram.js +12 -0
  87. package/lib-esm/shader/ShaderManager.js +12 -1
  88. package/lib-esm/shader/ShaderManagerMutliHiPS.js +12 -1
  89. package/lib-esm/shader/XYZShaderProgram.d.ts +33 -0
  90. package/lib-esm/shader/XYZShaderProgram.js +213 -0
  91. package/lib-esm/utils/CoordsType.js +12 -0
  92. package/lib-esm/utils/FoVUtils.js +12 -0
  93. package/lib-esm/utils/GeomUtils.js +12 -0
  94. package/lib-esm/utils/MouseHelper.js +12 -0
  95. package/lib-esm/utils/PerspectiveMatrixManager.js +12 -0
  96. package/lib-esm/utils/RayPickingUtils.js +12 -0
  97. package/lib-esm/utils/STCSParser.js +12 -0
  98. package/lib-esm/utils/ShaderUtility.js +13 -1
  99. package/lib-esm/utils/Utils.js +12 -0
  100. package/lib-esm/utils/XYZRayPickingUtils.d.ts +27 -0
  101. package/lib-esm/utils/XYZRayPickingUtils.js +226 -0
  102. package/package.json +1 -2
  103. package/lib-esm/model/FoV.d.ts +0 -62
  104. package/lib-esm/model/FoV.js +0 -274
@@ -0,0 +1,77 @@
1
+ /*
2
+ * AstroViewer
3
+ * Copyright (C) Fabrizio Giordano
4
+ * SPDX-License-Identifier: LicenseRef-AstroViewer-Dual-License
5
+ *
6
+ * This file is part of AstroViewer.
7
+ * AstroViewer is distributed under a dual-license model.
8
+ * Commercial use requires a separate commercial license.
9
+ * Non-commercial use is governed by LICENSE-NONCOMMERCIAL.md.
10
+ *
11
+ * See LICENSE.md, LICENSE-COMMERCIAL.md, and LICENSE-NONCOMMERCIAL.md for details.
12
+ */
13
+ export class XYZMapDescriptor {
14
+ _name;
15
+ _url;
16
+ _urlResolver;
17
+ _minZoom;
18
+ _maxZoom;
19
+ _segmentsPerSide;
20
+ _tileSize;
21
+ _maxCachedTiles;
22
+ _interactionDebounceMs;
23
+ _subdomains;
24
+ _attribution;
25
+ _flipY;
26
+ _maxConcurrentLoads;
27
+ constructor(name, url, minZoom = 0, maxZoom = 8, segmentsPerSide = 16, maxCachedTiles = 384, maxConcurrentLoads = 8, urlResolver) {
28
+ this._name = name;
29
+ this._url = url;
30
+ this._urlResolver = urlResolver;
31
+ this._minZoom = minZoom;
32
+ this._maxZoom = maxZoom;
33
+ this._segmentsPerSide = segmentsPerSide;
34
+ this._maxCachedTiles = maxCachedTiles;
35
+ this._interactionDebounceMs = 100;
36
+ this._subdomains = ['a', 'b', 'c'];
37
+ this._attribution = '';
38
+ this._flipY = false;
39
+ this._maxConcurrentLoads = maxConcurrentLoads;
40
+ }
41
+ get url() {
42
+ return this._url;
43
+ }
44
+ get urlResolver() {
45
+ return this._urlResolver;
46
+ }
47
+ get name() {
48
+ return this._name;
49
+ }
50
+ get minZoom() {
51
+ return this._minZoom;
52
+ }
53
+ get maxZoom() {
54
+ return this._maxZoom;
55
+ }
56
+ get segmentsPerSide() {
57
+ return this._segmentsPerSide;
58
+ }
59
+ get maxCachedTiles() {
60
+ return this._maxCachedTiles;
61
+ }
62
+ get interactionDebounceMs() {
63
+ return this._interactionDebounceMs;
64
+ }
65
+ get subdomains() {
66
+ return this._subdomains;
67
+ }
68
+ get attribution() {
69
+ return this._attribution;
70
+ }
71
+ get flipY() {
72
+ return this._flipY;
73
+ }
74
+ get maxConcurrentLoads() {
75
+ return this._maxConcurrentLoads;
76
+ }
77
+ }
@@ -0,0 +1,6 @@
1
+ import type { XYZTileCoord, XYZTileGpuMesh, XYZTileMesh } from './XYZTypes.js';
2
+ export declare class XYZMeshBuilder {
3
+ buildTileMesh(tile: XYZTileCoord, segmentsPerSide?: number): XYZTileMesh;
4
+ buildAncestorMesh(targetTile: XYZTileCoord, ancestorTile: XYZTileCoord, segmentsPerSide?: number): XYZTileMesh;
5
+ uploadMesh(mesh: XYZTileMesh, webgl: WebGL2RenderingContext): XYZTileGpuMesh;
6
+ }
@@ -0,0 +1,108 @@
1
+ /*
2
+ * AstroViewer
3
+ * Copyright (C) Fabrizio Giordano
4
+ * SPDX-License-Identifier: LicenseRef-AstroViewer-Dual-License
5
+ *
6
+ * This file is part of AstroViewer.
7
+ * AstroViewer is distributed under a dual-license model.
8
+ * Commercial use requires a separate commercial license.
9
+ * Non-commercial use is governed by LICENSE-NONCOMMERCIAL.md.
10
+ *
11
+ * See LICENSE.md, LICENSE-COMMERCIAL.md, and LICENSE-NONCOMMERCIAL.md for details.
12
+ */
13
+ import { sphericalToCartesian } from '../../utils/Utils.js';
14
+ const MAX_MERCATOR_LAT = 85.0511287798066;
15
+ function mercatorYToLatDeg(yNormalized) {
16
+ const mercator = Math.PI * (1 - 2 * yNormalized);
17
+ return (Math.atan(Math.sinh(mercator)) * 180) / Math.PI;
18
+ }
19
+ function wrapLonToPhi(lonDeg) {
20
+ return lonDeg < 0 ? lonDeg + 360 : lonDeg;
21
+ }
22
+ export class XYZMeshBuilder {
23
+ buildTileMesh(tile, segmentsPerSide = 16) {
24
+ const segments = Math.max(1, Math.floor(segmentsPerSide));
25
+ const gridSize = segments + 1;
26
+ const vertexCount = gridSize * gridSize;
27
+ const positions = new Float32Array(vertexCount * 3);
28
+ const uvs = new Float32Array(vertexCount * 2);
29
+ const tileCount = 2 ** tile.z;
30
+ let p = 0;
31
+ let uv = 0;
32
+ for (let row = 0; row <= segments; row++) {
33
+ const v = row / segments;
34
+ const yNormalized = (tile.y + v) / tileCount;
35
+ const latDeg = Math.max(-MAX_MERCATOR_LAT, Math.min(MAX_MERCATOR_LAT, mercatorYToLatDeg(yNormalized)));
36
+ const thetaDeg = 90 - latDeg;
37
+ for (let col = 0; col <= segments; col++) {
38
+ const u = col / segments;
39
+ const xNormalized = (tile.x + u) / tileCount;
40
+ const lonDeg = xNormalized * 360 - 180;
41
+ const phiDeg = wrapLonToPhi(lonDeg);
42
+ const [x, y, z] = sphericalToCartesian(phiDeg, thetaDeg, 1);
43
+ positions[p++] = x;
44
+ positions[p++] = y;
45
+ positions[p++] = z;
46
+ uvs[uv++] = u;
47
+ uvs[uv++] = 1 - v;
48
+ }
49
+ }
50
+ const rawIndices = new Uint32Array(segments * segments * 2 * 3);
51
+ let i = 0;
52
+ for (let row = 0; row < segments; row++) {
53
+ for (let col = 0; col < segments; col++) {
54
+ const topLeft = row * gridSize + col;
55
+ const topRight = topLeft + 1;
56
+ const bottomLeft = topLeft + gridSize;
57
+ const bottomRight = bottomLeft + 1;
58
+ rawIndices[i++] = topLeft;
59
+ rawIndices[i++] = bottomLeft;
60
+ rawIndices[i++] = topRight;
61
+ rawIndices[i++] = topRight;
62
+ rawIndices[i++] = bottomLeft;
63
+ rawIndices[i++] = bottomRight;
64
+ }
65
+ }
66
+ const indices = rawIndices.length > 65535 ? rawIndices : new Uint16Array(rawIndices);
67
+ return { positions, uvs, indices };
68
+ }
69
+ buildAncestorMesh(targetTile, ancestorTile, segmentsPerSide = 16) {
70
+ const baseMesh = this.buildTileMesh(targetTile, segmentsPerSide);
71
+ const dz = targetTile.z - ancestorTile.z;
72
+ const scale = 2 ** dz;
73
+ const subTileX = targetTile.x - (ancestorTile.x << dz);
74
+ const subTileY = targetTile.y - (ancestorTile.y << dz);
75
+ const uvs = new Float32Array(baseMesh.uvs.length);
76
+ for (let i = 0; i < baseMesh.uvs.length; i += 2) {
77
+ const u = baseMesh.uvs[i] ?? 0;
78
+ const baseV = baseMesh.uvs[i + 1] ?? 0;
79
+ const v = 1 - baseV;
80
+ uvs[i] = (subTileX + u) / scale;
81
+ uvs[i + 1] = 1 - (subTileY + v) / scale;
82
+ }
83
+ return {
84
+ positions: baseMesh.positions,
85
+ uvs,
86
+ indices: baseMesh.indices,
87
+ };
88
+ }
89
+ uploadMesh(mesh, webgl) {
90
+ const positionBuffer = webgl.createBuffer();
91
+ const uvBuffer = webgl.createBuffer();
92
+ const indexBuffer = webgl.createBuffer();
93
+ const indexType = mesh.indices instanceof Uint32Array ? webgl.UNSIGNED_INT : webgl.UNSIGNED_SHORT;
94
+ webgl.bindBuffer(webgl.ARRAY_BUFFER, positionBuffer);
95
+ webgl.bufferData(webgl.ARRAY_BUFFER, mesh.positions, webgl.STATIC_DRAW);
96
+ webgl.bindBuffer(webgl.ARRAY_BUFFER, uvBuffer);
97
+ webgl.bufferData(webgl.ARRAY_BUFFER, mesh.uvs, webgl.STATIC_DRAW);
98
+ webgl.bindBuffer(webgl.ELEMENT_ARRAY_BUFFER, indexBuffer);
99
+ webgl.bufferData(webgl.ELEMENT_ARRAY_BUFFER, mesh.indices, webgl.STATIC_DRAW);
100
+ return {
101
+ positionBuffer,
102
+ uvBuffer,
103
+ indexBuffer,
104
+ indexCount: mesh.indices.length,
105
+ indexType,
106
+ };
107
+ }
108
+ }
@@ -0,0 +1,38 @@
1
+ import { XYZShaderProgram } from '../../shader/XYZShaderProgram.js';
2
+ import type { XYZBufferedTile } from './XYZTileBuffer.js';
3
+ import { XYZMeshBuilder } from './XYZMeshBuilder.js';
4
+ import type { XYZTileCoord, XYZTileGpuMesh } from './XYZTypes.js';
5
+ type Mat4 = Float32Array;
6
+ export declare class XYZTile implements XYZBufferedTile {
7
+ private _coord;
8
+ private _url;
9
+ private _webgl;
10
+ private _shaderProgram;
11
+ private _meshBuilder;
12
+ private _gpuMesh;
13
+ private _texture;
14
+ private _image?;
15
+ private _ready;
16
+ private _loading;
17
+ private _aborted;
18
+ private _failedUntil;
19
+ private _lastUsedAt;
20
+ private _createdAt;
21
+ constructor(coord: XYZTileCoord, url: string, webgl: WebGL2RenderingContext, shaderProgram: XYZShaderProgram, meshBuilder?: XYZMeshBuilder, segmentsPerSide?: number);
22
+ get coord(): XYZTileCoord;
23
+ get ready(): boolean;
24
+ get loading(): boolean;
25
+ get failedUntil(): number;
26
+ get lastUsedAt(): number;
27
+ get createdAt(): number;
28
+ touch(): void;
29
+ private initImage;
30
+ private imageLoaded;
31
+ private textureLoaded;
32
+ draw(pMatrix: Mat4, vMatrix: Mat4, mMatrix: Mat4, colorMapIdx: number): boolean;
33
+ drawRemapped(mesh: XYZTileGpuMesh, pMatrix: Mat4, vMatrix: Mat4, mMatrix: Mat4, colorMapIdx: number): boolean;
34
+ dispose(): void;
35
+ private drawWithGpuMesh;
36
+ private get key();
37
+ }
38
+ export {};
@@ -0,0 +1,167 @@
1
+ /*
2
+ * AstroViewer
3
+ * Copyright (C) Fabrizio Giordano
4
+ * SPDX-License-Identifier: LicenseRef-AstroViewer-Dual-License
5
+ *
6
+ * This file is part of AstroViewer.
7
+ * AstroViewer is distributed under a dual-license model.
8
+ * Commercial use requires a separate commercial license.
9
+ * Non-commercial use is governed by LICENSE-NONCOMMERCIAL.md.
10
+ *
11
+ * See LICENSE.md, LICENSE-COMMERCIAL.md, and LICENSE-NONCOMMERCIAL.md for details.
12
+ */
13
+ import { XYZMeshBuilder } from './XYZMeshBuilder.js';
14
+ export class XYZTile {
15
+ _coord;
16
+ _url;
17
+ _webgl;
18
+ _shaderProgram;
19
+ _meshBuilder;
20
+ _gpuMesh;
21
+ _texture = null;
22
+ _image;
23
+ _ready = false;
24
+ _loading = false;
25
+ _aborted = false;
26
+ _failedUntil = 0;
27
+ _lastUsedAt = 0;
28
+ _createdAt = Date.now();
29
+ constructor(coord, url, webgl, shaderProgram, meshBuilder = new XYZMeshBuilder(), segmentsPerSide = 16) {
30
+ this._coord = coord;
31
+ this._url = url;
32
+ this._webgl = webgl;
33
+ this._shaderProgram = shaderProgram;
34
+ this._meshBuilder = meshBuilder;
35
+ const mesh = this._meshBuilder.buildTileMesh(coord, segmentsPerSide);
36
+ this._gpuMesh = this._meshBuilder.uploadMesh(mesh, this._webgl);
37
+ this.initImage();
38
+ }
39
+ get coord() {
40
+ return this._coord;
41
+ }
42
+ get ready() {
43
+ return this._ready;
44
+ }
45
+ get loading() {
46
+ return this._loading;
47
+ }
48
+ get failedUntil() {
49
+ return this._failedUntil;
50
+ }
51
+ get lastUsedAt() {
52
+ return this._lastUsedAt;
53
+ }
54
+ get createdAt() {
55
+ return this._createdAt;
56
+ }
57
+ touch() {
58
+ this._lastUsedAt = Date.now();
59
+ }
60
+ initImage() {
61
+ if (this._loading || this._ready || this._aborted) {
62
+ return;
63
+ }
64
+ const now = Date.now();
65
+ if (this._failedUntil > now) {
66
+ return;
67
+ }
68
+ this._loading = true;
69
+ const image = new Image();
70
+ this._image = image;
71
+ image.crossOrigin = 'anonymous';
72
+ image.onload = () => this.imageLoaded();
73
+ image.onerror = () => {
74
+ this._ready = false;
75
+ this._loading = false;
76
+ this._failedUntil = Date.now() + 30_000;
77
+ };
78
+ image.src = this._url;
79
+ }
80
+ imageLoaded() {
81
+ if (!this._image || this._aborted) {
82
+ return;
83
+ }
84
+ this.textureLoaded(this._image);
85
+ this._loading = false;
86
+ this._failedUntil = 0;
87
+ this._ready = true;
88
+ }
89
+ textureLoaded(image) {
90
+ const gl = this._webgl;
91
+ this._shaderProgram.enableProgram();
92
+ const texture = gl.createTexture();
93
+ if (!texture) {
94
+ throw new Error(`Could not create XYZ texture for ${this.key}`);
95
+ }
96
+ this._texture = texture;
97
+ gl.activeTexture(gl.TEXTURE0);
98
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
99
+ gl.bindTexture(gl.TEXTURE_2D, texture);
100
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
101
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
102
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
103
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
104
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
105
+ }
106
+ draw(pMatrix, vMatrix, mMatrix, colorMapIdx) {
107
+ this.touch();
108
+ if (!this._ready || !this._texture || this._aborted) {
109
+ return false;
110
+ }
111
+ this.drawWithGpuMesh(this._gpuMesh, pMatrix, vMatrix, mMatrix, colorMapIdx);
112
+ return true;
113
+ }
114
+ drawRemapped(mesh, pMatrix, vMatrix, mMatrix, colorMapIdx) {
115
+ this.touch();
116
+ if (!this._ready || !this._texture || this._aborted) {
117
+ return false;
118
+ }
119
+ this.drawWithGpuMesh(mesh, pMatrix, vMatrix, mMatrix, colorMapIdx);
120
+ return true;
121
+ }
122
+ dispose() {
123
+ const gl = this._webgl;
124
+ if (this._texture) {
125
+ gl.deleteTexture(this._texture);
126
+ this._texture = null;
127
+ }
128
+ if (this._gpuMesh.positionBuffer) {
129
+ gl.deleteBuffer(this._gpuMesh.positionBuffer);
130
+ this._gpuMesh.positionBuffer = null;
131
+ }
132
+ if (this._gpuMesh.uvBuffer) {
133
+ gl.deleteBuffer(this._gpuMesh.uvBuffer);
134
+ this._gpuMesh.uvBuffer = null;
135
+ }
136
+ if (this._gpuMesh.indexBuffer) {
137
+ gl.deleteBuffer(this._gpuMesh.indexBuffer);
138
+ this._gpuMesh.indexBuffer = null;
139
+ }
140
+ this._image = undefined;
141
+ this._ready = false;
142
+ this._loading = false;
143
+ this._aborted = true;
144
+ }
145
+ drawWithGpuMesh(mesh, pMatrix, vMatrix, mMatrix, colorMapIdx) {
146
+ if (!this._texture) {
147
+ return;
148
+ }
149
+ const gl = this._webgl;
150
+ this._shaderProgram.enableShaders(pMatrix, vMatrix, mMatrix, colorMapIdx);
151
+ gl.bindBuffer(gl.ARRAY_BUFFER, mesh.positionBuffer);
152
+ gl.vertexAttribPointer(this._shaderProgram.locations.vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
153
+ gl.enableVertexAttribArray(this._shaderProgram.locations.vertexPositionAttribute);
154
+ gl.bindBuffer(gl.ARRAY_BUFFER, mesh.uvBuffer);
155
+ gl.vertexAttribPointer(this._shaderProgram.locations.textureCoordAttribute, 2, gl.FLOAT, false, 0, 0);
156
+ gl.enableVertexAttribArray(this._shaderProgram.locations.textureCoordAttribute);
157
+ gl.activeTexture(gl.TEXTURE0);
158
+ gl.bindTexture(gl.TEXTURE_2D, this._texture);
159
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, mesh.indexBuffer);
160
+ gl.drawElements(gl.TRIANGLES, mesh.indexCount, mesh.indexType, 0);
161
+ gl.disableVertexAttribArray(this._shaderProgram.locations.vertexPositionAttribute);
162
+ gl.disableVertexAttribArray(this._shaderProgram.locations.textureCoordAttribute);
163
+ }
164
+ get key() {
165
+ return `${this._coord.z}/${this._coord.x}/${this._coord.y}`;
166
+ }
167
+ }
@@ -0,0 +1,37 @@
1
+ import type { XYZTileCoord } from './XYZTypes.js';
2
+ export type XYZBufferedTile = {
3
+ coord: XYZTileCoord;
4
+ loading?: boolean;
5
+ lastUsedAt?: number;
6
+ createdAt?: number;
7
+ touch?: () => void;
8
+ dispose?: () => void;
9
+ };
10
+ export type XYZTileBufferEntry<TTile extends XYZBufferedTile = XYZBufferedTile> = {
11
+ tile: TTile;
12
+ cacheTime0?: number;
13
+ };
14
+ export type XYZTileFactory<TTile extends XYZBufferedTile = XYZBufferedTile> = (coord: XYZTileCoord) => TTile;
15
+ export declare class XYZTileBuffer<TTile extends XYZBufferedTile = XYZBufferedTile> {
16
+ private _tiles;
17
+ private _cachedTiles;
18
+ private _cacheAliveMilliSeconds;
19
+ private _cleanerId;
20
+ constructor(minutesToLiveInCache?: number);
21
+ get activeTiles(): Map<string, XYZTileBufferEntry<TTile>>;
22
+ get cachedTiles(): Map<string, XYZTileBufferEntry<TTile>>;
23
+ get size(): number;
24
+ ensureTiles(visibleTiles: XYZTileCoord[], tileFactory: XYZTileFactory<TTile>): string[];
25
+ getTile(tileCoord: XYZTileCoord, tileFactory: XYZTileFactory<TTile>): TTile;
26
+ getActiveTile(tileKey: string): TTile | null;
27
+ getAnyTile(tileKey: string): TTile | null;
28
+ getActiveTiles(): TTile[];
29
+ syncVisibleTiles(visibleTileKeys: string[]): void;
30
+ evictCached(maxCachedTiles: number): void;
31
+ dispose(): void;
32
+ key(tileCoord: XYZTileCoord): string;
33
+ static key(tileCoord: XYZTileCoord): string;
34
+ private cacheCleaner;
35
+ private touchTile;
36
+ private getTileAgeScore;
37
+ }
@@ -0,0 +1,139 @@
1
+ /*
2
+ * AstroViewer
3
+ * Copyright (C) Fabrizio Giordano
4
+ * SPDX-License-Identifier: LicenseRef-AstroViewer-Dual-License
5
+ *
6
+ * This file is part of AstroViewer.
7
+ * AstroViewer is distributed under a dual-license model.
8
+ * Commercial use requires a separate commercial license.
9
+ * Non-commercial use is governed by LICENSE-NONCOMMERCIAL.md.
10
+ *
11
+ * See LICENSE.md, LICENSE-COMMERCIAL.md, and LICENSE-NONCOMMERCIAL.md for details.
12
+ */
13
+ export class XYZTileBuffer {
14
+ _tiles = new Map();
15
+ _cachedTiles = new Map();
16
+ _cacheAliveMilliSeconds;
17
+ _cleanerId;
18
+ constructor(minutesToLiveInCache = 1) {
19
+ this._cacheAliveMilliSeconds = minutesToLiveInCache * 60 * 1000;
20
+ if (typeof window !== 'undefined') {
21
+ this._cleanerId = window.setInterval(() => {
22
+ this.cacheCleaner();
23
+ }, 10_000);
24
+ }
25
+ }
26
+ get activeTiles() {
27
+ return this._tiles;
28
+ }
29
+ get cachedTiles() {
30
+ return this._cachedTiles;
31
+ }
32
+ get size() {
33
+ return this._tiles.size + this._cachedTiles.size;
34
+ }
35
+ ensureTiles(visibleTiles, tileFactory) {
36
+ const visibleTileKeys = [];
37
+ for (const tileCoord of visibleTiles) {
38
+ const tile = this.getTile(tileCoord, tileFactory);
39
+ this.touchTile(tile);
40
+ visibleTileKeys.push(this.key(tileCoord));
41
+ }
42
+ this.syncVisibleTiles(visibleTileKeys);
43
+ return visibleTileKeys;
44
+ }
45
+ getTile(tileCoord, tileFactory) {
46
+ const tileKey = this.key(tileCoord);
47
+ if (this._tiles.has(tileKey)) {
48
+ return this._tiles.get(tileKey).tile;
49
+ }
50
+ if (this._cachedTiles.has(tileKey)) {
51
+ const entry = this._cachedTiles.get(tileKey);
52
+ entry.cacheTime0 = undefined;
53
+ this._tiles.set(tileKey, entry);
54
+ this._cachedTiles.delete(tileKey);
55
+ return entry.tile;
56
+ }
57
+ const tile = tileFactory(tileCoord);
58
+ this._tiles.set(tileKey, { tile });
59
+ return tile;
60
+ }
61
+ getActiveTile(tileKey) {
62
+ return this._tiles.get(tileKey)?.tile ?? null;
63
+ }
64
+ getAnyTile(tileKey) {
65
+ return this._tiles.get(tileKey)?.tile ?? this._cachedTiles.get(tileKey)?.tile ?? null;
66
+ }
67
+ getActiveTiles() {
68
+ return Array.from(this._tiles.values(), (entry) => entry.tile);
69
+ }
70
+ syncVisibleTiles(visibleTileKeys) {
71
+ const visibleKeySet = new Set(visibleTileKeys);
72
+ for (const [tileKey, entry] of this._tiles) {
73
+ if (visibleKeySet.has(tileKey)) {
74
+ this.touchTile(entry.tile);
75
+ continue;
76
+ }
77
+ entry.cacheTime0 = Date.now();
78
+ this._cachedTiles.set(tileKey, entry);
79
+ this._tiles.delete(tileKey);
80
+ }
81
+ }
82
+ evictCached(maxCachedTiles) {
83
+ if (this.size <= maxCachedTiles) {
84
+ return;
85
+ }
86
+ const candidates = Array.from(this._cachedTiles.entries()).sort((a, b) => {
87
+ return this.getTileAgeScore(a[1].tile) - this.getTileAgeScore(b[1].tile);
88
+ });
89
+ for (const [tileKey, entry] of candidates) {
90
+ if (this.size <= maxCachedTiles) {
91
+ break;
92
+ }
93
+ if (entry.tile.loading) {
94
+ continue;
95
+ }
96
+ entry.tile.dispose?.();
97
+ this._cachedTiles.delete(tileKey);
98
+ }
99
+ }
100
+ dispose() {
101
+ if (this._cleanerId !== undefined && typeof window !== 'undefined') {
102
+ window.clearInterval(this._cleanerId);
103
+ this._cleanerId = undefined;
104
+ }
105
+ for (const entry of this._tiles.values()) {
106
+ entry.tile.dispose?.();
107
+ }
108
+ for (const entry of this._cachedTiles.values()) {
109
+ entry.tile.dispose?.();
110
+ }
111
+ this._tiles.clear();
112
+ this._cachedTiles.clear();
113
+ }
114
+ key(tileCoord) {
115
+ return XYZTileBuffer.key(tileCoord);
116
+ }
117
+ static key(tileCoord) {
118
+ return `${tileCoord.z}/${tileCoord.x}/${tileCoord.y}`;
119
+ }
120
+ cacheCleaner() {
121
+ const now = Date.now();
122
+ for (const [tileKey, entry] of this._cachedTiles) {
123
+ const t0 = entry.cacheTime0;
124
+ if (t0 === undefined || now - t0 <= this._cacheAliveMilliSeconds || entry.tile.loading) {
125
+ continue;
126
+ }
127
+ entry.tile.dispose?.();
128
+ this._cachedTiles.delete(tileKey);
129
+ }
130
+ }
131
+ touchTile(tile) {
132
+ tile.touch?.();
133
+ }
134
+ getTileAgeScore(tile) {
135
+ const lastUsedAt = tile.lastUsedAt ?? 0;
136
+ const createdAt = tile.createdAt ?? lastUsedAt;
137
+ return Math.min(lastUsedAt || createdAt, createdAt);
138
+ }
139
+ }
@@ -0,0 +1,25 @@
1
+ import type { XYZRequestSchedulerDebugStats } from './XYZConfig.js';
2
+ export declare class XYZTileRequestError extends Error {
3
+ cooldownMs: number;
4
+ retryable: boolean;
5
+ constructor(message: string, cooldownMs: number, retryable?: boolean);
6
+ }
7
+ export declare class XYZTileRequestScheduler {
8
+ private _maxConcurrent;
9
+ private _activeCount;
10
+ private _queue;
11
+ private _inflight;
12
+ private _hostBackoff;
13
+ private _sequence;
14
+ constructor(maxConcurrent?: number);
15
+ setMaxConcurrent(maxConcurrent: number): void;
16
+ getMaxConcurrent(): number;
17
+ getDebugStats(): XYZRequestSchedulerDebugStats;
18
+ load(url: string, priority?: number): Promise<Blob>;
19
+ private pump;
20
+ private fetchBlob;
21
+ private getHostCooldown;
22
+ private registerSuccess;
23
+ private registerFailure;
24
+ }
25
+ export declare const xyzTileRequestScheduler: XYZTileRequestScheduler;