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,97 @@
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
+ function replaceTokens(template, values) {
14
+ return Object.entries(values).reduce((result, [key, value]) => result.replace(new RegExp(`\\{${key}\\}`, 'g'), value), template);
15
+ }
16
+ export class WMTSAdapter {
17
+ _config;
18
+ constructor(config) {
19
+ this._config = config;
20
+ }
21
+ toXYZLayerConfig() {
22
+ const inferredMaxZoom = this.getInferredMaxZoom();
23
+ return {
24
+ urlTemplate: this._config.urlTemplate ?? this._config.baseUrl,
25
+ minZoom: this._config.minZoom,
26
+ maxZoom: inferredMaxZoom,
27
+ segmentsPerSide: this._config.segmentsPerSide,
28
+ tileSize: this._config.tileSize,
29
+ maxCachedTiles: this._config.maxCachedTiles,
30
+ subdomains: this._config.subdomains,
31
+ attribution: this._config.attribution,
32
+ flipY: this._config.flipY,
33
+ urlResolver: (tile) => this.getTileUrl(tile),
34
+ };
35
+ }
36
+ getInferredMaxZoom() {
37
+ const matrixLabelCount = this._config.matrixLabels?.length ?? 0;
38
+ const maxFromLabels = matrixLabelCount > 0 ? matrixLabelCount - 1 : undefined;
39
+ if (maxFromLabels == null) {
40
+ return this._config.maxZoom;
41
+ }
42
+ if (this._config.maxZoom == null) {
43
+ return maxFromLabels;
44
+ }
45
+ return Math.min(this._config.maxZoom, maxFromLabels);
46
+ }
47
+ getTileUrl(tile) {
48
+ return this._config.requestEncoding === 'rest'
49
+ ? this.buildRestUrl(tile)
50
+ : this.buildKvpUrl(tile);
51
+ }
52
+ buildRestUrl(tile) {
53
+ const template = this._config.urlTemplate ?? this._config.baseUrl;
54
+ const values = this.getCommonTokenValues(tile);
55
+ const resolved = replaceTokens(template, values);
56
+ return resolved.replace(/(?<!:)\/{2,}/g, '/');
57
+ }
58
+ buildKvpUrl(tile) {
59
+ const baseUrl = new URL(this._config.baseUrl);
60
+ const values = this.getCommonTokenValues(tile);
61
+ const params = baseUrl.searchParams;
62
+ params.set('SERVICE', 'WMTS');
63
+ params.set('REQUEST', 'GetTile');
64
+ params.set('VERSION', this._config.version ?? '1.0.0');
65
+ params.set('LAYER', this._config.layer);
66
+ params.set('STYLE', this._config.style ?? 'default');
67
+ params.set('FORMAT', this._config.format ?? 'image/png');
68
+ params.set('TILEMATRIXSET', this._config.tileMatrixSet);
69
+ params.set('TILEMATRIX', values.TileMatrix);
70
+ params.set('TILEROW', values.TileRow);
71
+ params.set('TILECOL', values.TileCol);
72
+ for (const [key, value] of Object.entries(this._config.dimensions ?? {})) {
73
+ params.set(key, value);
74
+ }
75
+ return baseUrl.toString();
76
+ }
77
+ getCommonTokenValues(tile) {
78
+ const dim = 2 ** tile.z;
79
+ const effectiveY = this._config.flipY ? dim - 1 - tile.y : tile.y;
80
+ const matrixLabel = this._config.matrixLabels?.[tile.z] ?? String(tile.z);
81
+ const tileFormat = this._config.format ?? 'image/png';
82
+ const tileFormatExtension = tileFormat.replace(/^image\//, '');
83
+ return {
84
+ Layer: this._config.layer,
85
+ Style: this._config.style ?? 'default',
86
+ Time: this._config.time ?? '',
87
+ TileMatrixSet: this._config.tileMatrixSet,
88
+ TileMatrix: matrixLabel,
89
+ TileRow: String(effectiveY),
90
+ TileCol: String(tile.x),
91
+ Format: tileFormatExtension,
92
+ TileFormat: tileFormat,
93
+ TileFormatExtension: tileFormatExtension,
94
+ ...Object.fromEntries(Object.entries(this._config.dimensions ?? {}).map(([key, value]) => [key, value])),
95
+ };
96
+ }
97
+ }
@@ -0,0 +1,17 @@
1
+ import { XYZShaderProgram } from '../../shader/XYZShaderProgram.js';
2
+ import { XYZMeshBuilder } from './XYZMeshBuilder.js';
3
+ import { XYZTile } from './XYZTile.js';
4
+ import type { XYZTileCoord } from './XYZTypes.js';
5
+ type Mat4 = Float32Array;
6
+ export declare class XYZAnchestorTile extends XYZTile {
7
+ private _ancestorMeshBuilder;
8
+ private _ancestorWebgl;
9
+ private _segmentsPerSide;
10
+ private _meshCache;
11
+ constructor(coord: XYZTileCoord, url: string, webgl: WebGL2RenderingContext, shaderProgram: XYZShaderProgram, meshBuilder?: XYZMeshBuilder, segmentsPerSide?: number);
12
+ draw(pMatrixOrVisibleZoom: Mat4 | number, vMatrixOrVisibleTiles: Mat4 | XYZTileCoord[], mMatrixOrAncestorsMap: Mat4 | Map<string, XYZTileCoord>, colorMapIdxOrPMatrix: number | Mat4, vMatrix?: Mat4, mMatrix?: Mat4, colorMapIdx?: number): boolean;
13
+ dispose(): void;
14
+ private getRemappedMesh;
15
+ private isAncestorOf;
16
+ }
17
+ export { XYZAnchestorTile as XYZAncestorTile };
@@ -0,0 +1,87 @@
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
+ import { XYZTile } from './XYZTile.js';
15
+ export class XYZAnchestorTile extends XYZTile {
16
+ _ancestorMeshBuilder;
17
+ _ancestorWebgl;
18
+ _segmentsPerSide;
19
+ _meshCache = new Map();
20
+ constructor(coord, url, webgl, shaderProgram, meshBuilder = new XYZMeshBuilder(), segmentsPerSide = 16) {
21
+ super(coord, url, webgl, shaderProgram, meshBuilder, segmentsPerSide);
22
+ this._ancestorMeshBuilder = meshBuilder;
23
+ this._ancestorWebgl = webgl;
24
+ this._segmentsPerSide = segmentsPerSide;
25
+ }
26
+ draw(pMatrixOrVisibleZoom, vMatrixOrVisibleTiles, mMatrixOrAncestorsMap, colorMapIdxOrPMatrix, vMatrix, mMatrix, colorMapIdx) {
27
+ if (typeof pMatrixOrVisibleZoom !== 'number') {
28
+ return super.draw(pMatrixOrVisibleZoom, vMatrixOrVisibleTiles, mMatrixOrAncestorsMap, colorMapIdxOrPMatrix);
29
+ }
30
+ if (!vMatrix || !mMatrix || colorMapIdx === undefined) {
31
+ return false;
32
+ }
33
+ const visibleZoom = pMatrixOrVisibleZoom;
34
+ const visibleTiles = vMatrixOrVisibleTiles;
35
+ const ancestorsMap = mMatrixOrAncestorsMap;
36
+ const pMatrix = colorMapIdxOrPMatrix;
37
+ let drawn = false;
38
+ if (visibleZoom <= this.coord.z) {
39
+ return super.draw(pMatrix, vMatrix, mMatrix, colorMapIdx);
40
+ }
41
+ for (const targetTile of visibleTiles) {
42
+ if (!this.isAncestorOf(targetTile)) {
43
+ continue;
44
+ }
45
+ const ancestorKey = `${this.coord.z}/${this.coord.x}/${this.coord.y}`;
46
+ if (!ancestorsMap.has(ancestorKey)) {
47
+ continue;
48
+ }
49
+ const mesh = this.getRemappedMesh(targetTile);
50
+ drawn = super.drawRemapped(mesh, pMatrix, vMatrix, mMatrix, colorMapIdx) || drawn;
51
+ }
52
+ return drawn;
53
+ }
54
+ dispose() {
55
+ for (const mesh of this._meshCache.values()) {
56
+ if (mesh.positionBuffer)
57
+ this._ancestorWebgl.deleteBuffer(mesh.positionBuffer);
58
+ if (mesh.uvBuffer)
59
+ this._ancestorWebgl.deleteBuffer(mesh.uvBuffer);
60
+ if (mesh.indexBuffer)
61
+ this._ancestorWebgl.deleteBuffer(mesh.indexBuffer);
62
+ }
63
+ this._meshCache.clear();
64
+ super.dispose();
65
+ }
66
+ getRemappedMesh(targetTile) {
67
+ const key = `${targetTile.z}/${targetTile.x}/${targetTile.y}->${this.coord.z}/${this.coord.x}/${this.coord.y}`;
68
+ const existing = this._meshCache.get(key);
69
+ if (existing) {
70
+ return existing;
71
+ }
72
+ const mesh = this._ancestorMeshBuilder.buildAncestorMesh(targetTile, this.coord, this._segmentsPerSide);
73
+ const uploaded = this._ancestorMeshBuilder.uploadMesh(mesh, this._ancestorWebgl);
74
+ this._meshCache.set(key, uploaded);
75
+ return uploaded;
76
+ }
77
+ isAncestorOf(targetTile) {
78
+ if (targetTile.z <= this.coord.z) {
79
+ return targetTile.z === this.coord.z
80
+ && targetTile.x === this.coord.x
81
+ && targetTile.y === this.coord.y;
82
+ }
83
+ const dz = targetTile.z - this.coord.z;
84
+ return (targetTile.x >> dz) === this.coord.x && (targetTile.y >> dz) === this.coord.y;
85
+ }
86
+ }
87
+ export { XYZAnchestorTile as XYZAncestorTile };
@@ -0,0 +1,72 @@
1
+ import type { XYZTileCoord } from './XYZTypes.js';
2
+ export type XYZLayerConfig = {
3
+ urlTemplate: string;
4
+ name?: string;
5
+ minZoom?: number;
6
+ maxZoom?: number;
7
+ segmentsPerSide?: number;
8
+ tileSize?: number;
9
+ maxCachedTiles?: number;
10
+ interactionDebounceMs?: number;
11
+ subdomains?: string[];
12
+ attribution?: string;
13
+ flipY?: boolean;
14
+ urlResolver?: (tile: XYZTileCoord) => string;
15
+ };
16
+ export type XYZLayerDebugStats = {
17
+ cacheSize: number;
18
+ visibleTileCount: number;
19
+ currentTileCount: number;
20
+ fallbackTileCount: number;
21
+ coreTileCount: number;
22
+ coverageTileCount: number;
23
+ readyTileCount: number;
24
+ loadingTileCount: number;
25
+ coolingDownTileCount: number;
26
+ currentZoom: number | null;
27
+ tileSelectionKey: string | null;
28
+ isSettling: boolean;
29
+ coarseTileCount: number;
30
+ hasPendingSelection: boolean;
31
+ pendingSelectionKey: string | null;
32
+ };
33
+ export type XYZRequestBackoffDebugEntry = {
34
+ host: string;
35
+ cooldownMs: number;
36
+ consecutiveFailures: number;
37
+ };
38
+ export type XYZRequestSchedulerDebugStats = {
39
+ activeRequests: number;
40
+ queuedRequests: number;
41
+ inflightRequests: number;
42
+ maxConcurrentRequests: number;
43
+ highestQueuedPriority: number | null;
44
+ hostsInBackoff: XYZRequestBackoffDebugEntry[];
45
+ };
46
+ export type XYZDebugStats = {
47
+ activeBaseLayer: 'hips' | 'xyz' | null;
48
+ layer: XYZLayerDebugStats | null;
49
+ requests: XYZRequestSchedulerDebugStats;
50
+ };
51
+ export type WMTSRequestEncoding = 'kvp' | 'rest';
52
+ export type WMTSLayerConfig = {
53
+ baseUrl: string;
54
+ layer: string;
55
+ tileMatrixSet: string;
56
+ style?: string;
57
+ time?: string;
58
+ format?: string;
59
+ requestEncoding?: WMTSRequestEncoding;
60
+ version?: string;
61
+ dimensions?: Record<string, string>;
62
+ matrixLabels?: string[];
63
+ urlTemplate?: string;
64
+ minZoom?: number;
65
+ maxZoom?: number;
66
+ segmentsPerSide?: number;
67
+ tileSize?: number;
68
+ maxCachedTiles?: number;
69
+ subdomains?: string[];
70
+ attribution?: string;
71
+ flipY?: boolean;
72
+ };
@@ -0,0 +1,13 @@
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 {};
@@ -0,0 +1,9 @@
1
+ declare class XYZFoVHelper {
2
+ getZoom(fov: number): number;
3
+ getLonLatSteps(fov: number): {
4
+ lonStep: number;
5
+ latStep: number;
6
+ };
7
+ }
8
+ export declare const xyzFovHelper: XYZFoVHelper;
9
+ export default XYZFoVHelper;
@@ -0,0 +1,101 @@
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
+ // FoVHelper.ts
14
+ 'use strict';
15
+ class XYZFoVHelper {
16
+ getZoom(fov) {
17
+ if (fov >= 179)
18
+ return 2;
19
+ if (fov >= 90)
20
+ return 3;
21
+ if (fov >= 30)
22
+ return 4;
23
+ if (fov >= 20)
24
+ return 5;
25
+ if (fov >= 6)
26
+ return 6;
27
+ if (fov >= 3.2)
28
+ return 7;
29
+ if (fov >= 1.6)
30
+ return 8;
31
+ if (fov >= 0.85)
32
+ return 9;
33
+ if (fov >= 0.42)
34
+ return 10;
35
+ if (fov >= 0.21)
36
+ return 11;
37
+ if (fov >= 0.12)
38
+ return 12;
39
+ if (fov >= 0.06)
40
+ return 13;
41
+ if (fov >= 0.015)
42
+ return 14;
43
+ return 15;
44
+ }
45
+ // used in grid drawing
46
+ getLonLatSteps(fov) {
47
+ let lonStep;
48
+ let latStep;
49
+ if (fov >= 179) {
50
+ lonStep = 10;
51
+ latStep = 10;
52
+ }
53
+ else if (fov >= 25) {
54
+ lonStep = 9;
55
+ latStep = 9;
56
+ }
57
+ else if (fov >= 12.5) {
58
+ lonStep = 8;
59
+ latStep = 8;
60
+ }
61
+ else if (fov >= 6) {
62
+ lonStep = 6;
63
+ latStep = 6;
64
+ }
65
+ else if (fov >= 3.2) {
66
+ lonStep = 5;
67
+ latStep = 5;
68
+ }
69
+ else if (fov >= 1.6) {
70
+ lonStep = 4;
71
+ latStep = 4;
72
+ }
73
+ else if (fov >= 0.85) {
74
+ lonStep = 3;
75
+ latStep = 3;
76
+ }
77
+ else if (fov >= 0.42) {
78
+ lonStep = 2;
79
+ latStep = 2;
80
+ }
81
+ else if (fov >= 0.21) {
82
+ lonStep = 1;
83
+ latStep = 1;
84
+ }
85
+ else if (fov >= 0.12) {
86
+ lonStep = 0.5;
87
+ latStep = 0.5;
88
+ }
89
+ else if (fov >= 0.06) {
90
+ lonStep = 0.25;
91
+ latStep = 0.25;
92
+ }
93
+ else {
94
+ lonStep = 10;
95
+ latStep = 10;
96
+ }
97
+ return { lonStep, latStep };
98
+ }
99
+ }
100
+ export const xyzFovHelper = new XYZFoVHelper();
101
+ export default XYZFoVHelper;
@@ -0,0 +1,31 @@
1
+ import { AbstractSkyEntity, SkyEntityDrawInput } from "../AbstractSkyEntity.js";
2
+ import { XYZMapDescriptor } from "./XYZMapDescriptor.js";
3
+ import { ColorMap } from "../ColorMaps.js";
4
+ import { SphereFoV } from "../SphereFoV.js";
5
+ import type { XYZLayerDebugStats } from "./XYZConfig.js";
6
+ export declare class XYZMap extends AbstractSkyEntity {
7
+ private _xyzShaderProgram;
8
+ private _descriptor;
9
+ private _visibleTilesManager;
10
+ private _tileBuffer;
11
+ private _meshBuilder;
12
+ private _baseurl;
13
+ private _zoom;
14
+ private _latLonGrid;
15
+ private _colorMapIdx;
16
+ private _colorMap;
17
+ constructor(radius: number, position: [number, number, number], xrad: number, yrad: number, descriptor: XYZMapDescriptor, webgl: WebGL2RenderingContext);
18
+ changeColorMap(colorMap: ColorMap): void;
19
+ private initShaders;
20
+ isLonLatGridVisible(): boolean;
21
+ toggleLonLatGrid(): boolean;
22
+ getFoV(): SphereFoV;
23
+ private refresh;
24
+ draw(input: SkyEntityDrawInput): void;
25
+ private createTile;
26
+ private getTilesToEnsure;
27
+ private findBestAvailableAncestor;
28
+ private resolveTileUrl;
29
+ getDebugStats(): XYZLayerDebugStats;
30
+ private tileKey;
31
+ }
@@ -0,0 +1,208 @@
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 { AbstractSkyEntity } from "../AbstractSkyEntity.js";
14
+ import { XYZVisibleTilesManager } from "./XYZVisibleTilesManager.js";
15
+ import { xyzFovHelper } from "./XYZFoVHelper.js";
16
+ import { ColorMaps } from "../ColorMaps.js";
17
+ import { LatLonGrid } from "../grid/LonLatGrid.js";
18
+ import { XYZShaderProgram } from "../../shader/XYZShaderProgram.js";
19
+ import { XYZTileBuffer } from "./XYZTileBuffer.js";
20
+ import { XYZAnchestorTile } from "./XYZAnchestorTile.js";
21
+ import { XYZMeshBuilder } from "./XYZMeshBuilder.js";
22
+ export class XYZMap extends AbstractSkyEntity {
23
+ _xyzShaderProgram;
24
+ _descriptor;
25
+ _visibleTilesManager;
26
+ _tileBuffer;
27
+ _meshBuilder;
28
+ _baseurl;
29
+ _zoom;
30
+ _latLonGrid;
31
+ _colorMapIdx = 0;
32
+ _colorMap = ColorMaps['native'];
33
+ constructor(radius, position, xrad, yrad, descriptor, webgl) {
34
+ super(radius, position, xrad, yrad, descriptor.name, webgl, false);
35
+ this._descriptor = descriptor;
36
+ this._xyzShaderProgram = new XYZShaderProgram(webgl);
37
+ this._meshBuilder = new XYZMeshBuilder();
38
+ this._tileBuffer = new XYZTileBuffer(1);
39
+ this.initGL(webgl);
40
+ this._latLonGrid = new LatLonGrid(radius, position, xrad, yrad, 'LatLonGrid', this._webgl);
41
+ this._visibleTilesManager = new XYZVisibleTilesManager();
42
+ this._baseurl = descriptor.url;
43
+ this.initShaders();
44
+ const fov = 180;
45
+ this._zoom = xyzFovHelper.getZoom(fov);
46
+ }
47
+ changeColorMap(colorMap) {
48
+ this._colorMap = colorMap;
49
+ switch (colorMap.name) {
50
+ case 'grayscale':
51
+ this._colorMapIdx = 1;
52
+ this._colorMap = ColorMaps['grayscale'];
53
+ break;
54
+ case 'planck':
55
+ this._colorMapIdx = 2;
56
+ this._colorMap = ColorMaps['planck'];
57
+ break;
58
+ case 'cmb':
59
+ this._colorMapIdx = 3;
60
+ this._colorMap = ColorMaps['cmb'];
61
+ break;
62
+ case 'rainbow':
63
+ this._colorMapIdx = 4;
64
+ this._colorMap = ColorMaps['rainbow'];
65
+ break;
66
+ case 'eosb':
67
+ this._colorMapIdx = 5;
68
+ this._colorMap = ColorMaps['eosb'];
69
+ break;
70
+ case 'cubehelix':
71
+ this._colorMapIdx = 6;
72
+ this._colorMap = ColorMaps['cubehelix'];
73
+ break;
74
+ case 'hot':
75
+ this._colorMapIdx = 7;
76
+ this._colorMap = ColorMaps['hot'];
77
+ break;
78
+ case 'gray':
79
+ this._colorMapIdx = 8;
80
+ this._colorMap = ColorMaps['gray'];
81
+ break;
82
+ case 'native':
83
+ this._colorMapIdx = 0;
84
+ this._colorMap = ColorMaps['native'];
85
+ break;
86
+ default:
87
+ this._colorMapIdx = 9;
88
+ this._colorMap = colorMap;
89
+ }
90
+ }
91
+ initShaders() {
92
+ this._xyzShaderProgram.enableProgram();
93
+ }
94
+ isLonLatGridVisible() {
95
+ return this._latLonGrid.isVisible();
96
+ }
97
+ toggleLonLatGrid() {
98
+ return this._latLonGrid.toggleShowGrid();
99
+ }
100
+ getFoV() {
101
+ return this._latLonGrid.getFoV();
102
+ }
103
+ refresh(input) {
104
+ const fov = this._latLonGrid.refreshFoV(input);
105
+ // this._zoom = this.resolveVisibleZoom(fov)
106
+ this._zoom = xyzFovHelper.getZoom(fov);
107
+ }
108
+ draw(input) {
109
+ const vMatrix = input.camera.getCameraMatrix();
110
+ if (!vMatrix)
111
+ return;
112
+ const pMatrix = input.pMatrix;
113
+ if (!pMatrix)
114
+ return;
115
+ this.refresh(input);
116
+ const mMatrix = this.getModelMatrix();
117
+ this._xyzShaderProgram.setRuntimeColorMap(this._colorMap);
118
+ const tileSelection = this._visibleTilesManager.computeVisibleTiles(this._zoom, this, this._webgl, input.camera, input.pMatrix);
119
+ const visibleTiles = tileSelection.visibleTiles;
120
+ const ancestorsMap = tileSelection.ancestorsMap;
121
+ const tileKeys = this._tileBuffer.ensureTiles(this.getTilesToEnsure(visibleTiles, ancestorsMap), (coord) => this.createTile(coord));
122
+ this._tileBuffer.evictCached(this._descriptor.maxCachedTiles);
123
+ for (const tileKey of tileKeys) {
124
+ const tile = this._tileBuffer.getActiveTile(tileKey);
125
+ if (!tile || tile.coord.z !== tileSelection.currentZoom) {
126
+ continue;
127
+ }
128
+ const drawn = tile.draw(pMatrix, vMatrix, mMatrix, this._colorMapIdx);
129
+ if (drawn) {
130
+ continue;
131
+ }
132
+ const ancestorTile = this.findBestAvailableAncestor(tile.coord);
133
+ ancestorTile?.draw(tileSelection.currentZoom, [tile.coord], ancestorsMap, pMatrix, vMatrix, mMatrix, this._colorMapIdx);
134
+ }
135
+ this._latLonGrid.draw(input);
136
+ }
137
+ createTile(coord) {
138
+ return new XYZAnchestorTile(coord, this.resolveTileUrl(coord), this._webgl, this._xyzShaderProgram, this._meshBuilder, this._descriptor.segmentsPerSide);
139
+ }
140
+ getTilesToEnsure(visibleTiles, ancestorsMap) {
141
+ const tilesByKey = new Map();
142
+ for (const tile of visibleTiles) {
143
+ tilesByKey.set(this.tileKey(tile), tile);
144
+ }
145
+ for (const ancestor of ancestorsMap.values()) {
146
+ tilesByKey.set(this.tileKey(ancestor), ancestor);
147
+ }
148
+ return Array.from(tilesByKey.values());
149
+ }
150
+ findBestAvailableAncestor(targetTile) {
151
+ for (let z = targetTile.z - 1; z >= 0; z--) {
152
+ const dz = targetTile.z - z;
153
+ const ancestorCoord = {
154
+ z,
155
+ x: targetTile.x >> dz,
156
+ y: targetTile.y >> dz,
157
+ };
158
+ const ancestorTile = this._tileBuffer.getAnyTile(this.tileKey(ancestorCoord));
159
+ if (ancestorTile?.ready) {
160
+ return ancestorTile;
161
+ }
162
+ }
163
+ return null;
164
+ }
165
+ resolveTileUrl(tile) {
166
+ const urlResolver = this._descriptor.urlResolver;
167
+ if (urlResolver) {
168
+ return urlResolver(tile);
169
+ }
170
+ const dim = 2 ** tile.z;
171
+ const y = this._descriptor.flipY ? dim - 1 - tile.y : tile.y;
172
+ const subdomains = this._descriptor.subdomains;
173
+ const subdomain = subdomains.length > 0
174
+ ? subdomains[Math.abs(tile.x + tile.y + tile.z) % subdomains.length]
175
+ : '';
176
+ return this._baseurl
177
+ .replace(/\{z\}/g, String(tile.z))
178
+ .replace(/\{x\}/g, String(tile.x))
179
+ .replace(/\{y\}/g, String(y))
180
+ .replace(/\{s\}/g, subdomain ?? '');
181
+ }
182
+ getDebugStats() {
183
+ const activeTiles = Array.from(this._tileBuffer.activeTiles.values(), (entry) => entry.tile);
184
+ const cachedTiles = Array.from(this._tileBuffer.cachedTiles.values(), (entry) => entry.tile);
185
+ const allTiles = [...activeTiles, ...cachedTiles];
186
+ const selection = this._visibleTilesManager.selection;
187
+ return {
188
+ cacheSize: this._tileBuffer.size,
189
+ visibleTileCount: selection.visibleTiles.length,
190
+ currentTileCount: activeTiles.filter((tile) => tile.coord.z === selection.currentZoom).length,
191
+ fallbackTileCount: selection.ancestorsMap.size,
192
+ coreTileCount: selection.visibleTiles.length,
193
+ coverageTileCount: selection.visibleTiles.length,
194
+ readyTileCount: allTiles.filter((tile) => tile.ready).length,
195
+ loadingTileCount: allTiles.filter((tile) => tile.loading).length,
196
+ coolingDownTileCount: 0,
197
+ currentZoom: selection.currentZoom,
198
+ tileSelectionKey: selection.key,
199
+ isSettling: false,
200
+ coarseTileCount: selection.ancestorsMap.size,
201
+ hasPendingSelection: false,
202
+ pendingSelectionKey: null,
203
+ };
204
+ }
205
+ tileKey(tile) {
206
+ return `${tile.z}/${tile.x}/${tile.y}`;
207
+ }
208
+ }
@@ -0,0 +1,29 @@
1
+ import type { XYZTileCoord } from './XYZTypes.js';
2
+ export declare class XYZMapDescriptor {
3
+ _name: string;
4
+ _url: string;
5
+ _urlResolver?: (tile: XYZTileCoord) => string;
6
+ _minZoom?: number;
7
+ _maxZoom?: number;
8
+ _segmentsPerSide?: number;
9
+ _tileSize?: number;
10
+ _maxCachedTiles?: number;
11
+ _interactionDebounceMs?: number;
12
+ _subdomains?: string[];
13
+ _attribution?: string;
14
+ _flipY?: boolean;
15
+ _maxConcurrentLoads?: number;
16
+ constructor(name: string, url: string, minZoom?: number, maxZoom?: number, segmentsPerSide?: number, maxCachedTiles?: number, maxConcurrentLoads?: number, urlResolver?: (tile: XYZTileCoord) => string);
17
+ get url(): string;
18
+ get urlResolver(): ((tile: XYZTileCoord) => string) | undefined;
19
+ get name(): string;
20
+ get minZoom(): number;
21
+ get maxZoom(): number;
22
+ get segmentsPerSide(): number;
23
+ get maxCachedTiles(): number;
24
+ get interactionDebounceMs(): number;
25
+ get subdomains(): string[];
26
+ get attribution(): string;
27
+ get flipY(): boolean;
28
+ get maxConcurrentLoads(): number;
29
+ }