astro-viewer 3.0.0 → 3.2.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 (127) hide show
  1. package/dist/astroviewer.cjs +3082 -1410
  2. package/dist/astroviewer.js +1 -1
  3. package/dist/astroviewer.min.js +1 -1
  4. package/lib-esm/AstroSphere.d.ts +40 -16
  5. package/lib-esm/AstroSphere.js +359 -93
  6. package/lib-esm/AstroViewer.d.ts +20 -5
  7. package/lib-esm/AstroViewer.js +50 -0
  8. package/lib-esm/Camera.js +28 -14
  9. package/lib-esm/Config.d.ts +2 -0
  10. package/lib-esm/Config.js +15 -1
  11. package/lib-esm/Global.js +12 -0
  12. package/lib-esm/index.d.ts +8 -4
  13. package/lib-esm/index.js +17 -3
  14. package/lib-esm/model/AbstractSkyEntity.d.ts +1 -0
  15. package/lib-esm/model/AbstractSkyEntity.js +12 -0
  16. package/lib-esm/model/AstroGL.js +12 -0
  17. package/lib-esm/model/ColorMaps.js +12 -0
  18. package/lib-esm/model/MetadataColumn.js +12 -0
  19. package/lib-esm/model/MetadataManager.d.ts +1 -0
  20. package/lib-esm/model/MetadataManager.js +16 -1
  21. package/lib-esm/model/Point.d.ts +9 -1
  22. package/lib-esm/model/Point.js +30 -0
  23. package/lib-esm/model/Point2D.js +12 -0
  24. package/lib-esm/model/Source.js +12 -0
  25. package/lib-esm/model/SphereFoV.d.ts +30 -0
  26. package/lib-esm/model/SphereFoV.js +196 -0
  27. package/lib-esm/model/catalogues/CatalogueGL.js +12 -0
  28. package/lib-esm/model/catalogues/CatalogueProps.js +13 -1
  29. package/lib-esm/model/earth/{wmts/WMTSAdapter.d.ts → WMTSAdapter.d.ts} +2 -1
  30. package/lib-esm/model/earth/{wmts/WMTSAdapter.js → WMTSAdapter.js} +12 -0
  31. package/lib-esm/model/earth/XYZAnchestorTile.d.ts +17 -0
  32. package/lib-esm/model/earth/XYZAnchestorTile.js +87 -0
  33. package/lib-esm/model/earth/{types.d.ts → XYZConfig.d.ts} +6 -17
  34. package/lib-esm/model/earth/XYZConfig.js +13 -0
  35. package/lib-esm/model/earth/XYZFoVHelper.d.ts +12 -0
  36. package/lib-esm/model/earth/XYZFoVHelper.js +138 -0
  37. package/lib-esm/model/earth/XYZMap.d.ts +31 -0
  38. package/lib-esm/model/earth/XYZMap.js +208 -0
  39. package/lib-esm/model/earth/XYZMapDescriptor.d.ts +29 -0
  40. package/lib-esm/model/earth/XYZMapDescriptor.js +77 -0
  41. package/lib-esm/model/earth/XYZMeshBuilder.d.ts +1 -1
  42. package/lib-esm/model/earth/XYZMeshBuilder.js +13 -2
  43. package/lib-esm/model/earth/XYZTile.d.ts +20 -21
  44. package/lib-esm/model/earth/XYZTile.js +82 -102
  45. package/lib-esm/model/earth/XYZTileBuffer.d.ts +24 -26
  46. package/lib-esm/model/earth/XYZTileBuffer.js +60 -70
  47. package/lib-esm/model/earth/XYZTileRequestScheduler.d.ts +1 -1
  48. package/lib-esm/model/earth/XYZTileRequestScheduler.js +13 -2
  49. package/lib-esm/model/earth/XYZTypes.d.ts +24 -0
  50. package/lib-esm/model/earth/XYZTypes.js +13 -0
  51. package/lib-esm/model/earth/XYZVisibleTilesManager.d.ts +16 -27
  52. package/lib-esm/model/earth/XYZVisibleTilesManager.js +61 -192
  53. package/lib-esm/model/footprints/Footprint.d.ts +4 -1
  54. package/lib-esm/model/footprints/Footprint.js +30 -2
  55. package/lib-esm/model/footprints/FootprintProps.js +13 -1
  56. package/lib-esm/model/footprints/FootprintSetGL.d.ts +4 -2
  57. package/lib-esm/model/footprints/FootprintSetGL.js +21 -6
  58. package/lib-esm/model/grid/EquatorialGrid.d.ts +2 -1
  59. package/lib-esm/model/grid/EquatorialGrid.js +30 -12
  60. package/lib-esm/model/grid/GridTextHelper.d.ts +10 -19
  61. package/lib-esm/model/grid/GridTextHelper.js +70 -67
  62. package/lib-esm/model/grid/HealpixGrid.d.ts +4 -4
  63. package/lib-esm/model/grid/HealpixGrid.js +24 -8
  64. package/lib-esm/model/grid/LonLatGrid.d.ts +43 -0
  65. package/lib-esm/model/grid/LonLatGrid.js +309 -0
  66. package/lib-esm/model/hips/AllSky.js +12 -0
  67. package/lib-esm/model/hips/AncestorTile.js +12 -0
  68. package/lib-esm/model/hips/FoVHelper.d.ts +5 -2
  69. package/lib-esm/model/hips/FoVHelper.js +52 -3
  70. package/lib-esm/model/hips/HiPS.d.ts +2 -0
  71. package/lib-esm/model/hips/HiPS.js +38 -14
  72. package/lib-esm/model/hips/HiPSConfig.d.ts +13 -0
  73. package/lib-esm/model/hips/HiPSDescriptor.js +12 -0
  74. package/lib-esm/model/hips/Tile.d.ts +1 -0
  75. package/lib-esm/model/hips/Tile.js +15 -0
  76. package/lib-esm/model/hips/TileBuffer.d.ts +5 -0
  77. package/lib-esm/model/hips/TileBuffer.js +61 -0
  78. package/lib-esm/model/hips/VisibleTilesManager.js +12 -0
  79. package/lib-esm/model/tap/TapMetadata.js +12 -0
  80. package/lib-esm/model/tap/TapMetadataList.js +12 -0
  81. package/lib-esm/model/tap/TapRepo.js +12 -0
  82. package/lib-esm/model/terra/TerraFootprintSetGL.d.ts +6 -0
  83. package/lib-esm/model/terra/TerraFootprintSetGL.js +54 -0
  84. package/lib-esm/model/terra/TerraPointSetGL.js +12 -0
  85. package/lib-esm/services/SesameService.js +11 -3
  86. package/lib-esm/services/helpers.js +12 -0
  87. package/lib-esm/services/hipsNodeService.js +12 -0
  88. package/lib-esm/services/parse-hipslist.js +12 -0
  89. package/lib-esm/services/queryCatalogueByFoV.js +13 -1
  90. package/lib-esm/services/queryFootprintSetByFov.js +13 -1
  91. package/lib-esm/services/tapRepoService.js +13 -1
  92. package/lib-esm/services/types.js +12 -0
  93. package/lib-esm/shader/CatalogueShaderProgram.js +12 -0
  94. package/lib-esm/shader/FootprintShaderProgram.js +12 -0
  95. package/lib-esm/shader/GridShaderManager.js +12 -0
  96. package/lib-esm/shader/HiPSShaderProgram.js +12 -0
  97. package/lib-esm/shader/ShaderManager.js +12 -1
  98. package/lib-esm/shader/ShaderManagerMutliHiPS.js +12 -1
  99. package/lib-esm/shader/XYZShaderProgram.d.ts +14 -1
  100. package/lib-esm/shader/XYZShaderProgram.js +127 -2
  101. package/lib-esm/utils/CoordsType.d.ts +2 -1
  102. package/lib-esm/utils/CoordsType.js +13 -0
  103. package/lib-esm/utils/FoVUtils.js +12 -0
  104. package/lib-esm/utils/GeoJSONParser.d.ts +19 -0
  105. package/lib-esm/utils/GeoJSONParser.js +112 -0
  106. package/lib-esm/utils/GeomUtils.js +12 -0
  107. package/lib-esm/utils/MouseHelper.js +12 -0
  108. package/lib-esm/utils/PerspectiveMatrixManager.js +16 -1
  109. package/lib-esm/utils/RayPickingUtils.js +12 -0
  110. package/lib-esm/utils/STCSParser.d.ts +7 -3
  111. package/lib-esm/utils/STCSParser.js +27 -9
  112. package/lib-esm/utils/ShaderUtility.js +13 -1
  113. package/lib-esm/utils/Utils.js +12 -0
  114. package/lib-esm/utils/XYZRayPickingUtils.d.ts +27 -0
  115. package/lib-esm/utils/XYZRayPickingUtils.js +226 -0
  116. package/package.json +2 -2
  117. package/lib-esm/model/FoV.d.ts +0 -62
  118. package/lib-esm/model/FoV.js +0 -274
  119. package/lib-esm/model/earth/XYZAncestorMeshCache.d.ts +0 -10
  120. package/lib-esm/model/earth/XYZAncestorMeshCache.js +0 -31
  121. package/lib-esm/model/earth/XYZLayer.d.ts +0 -31
  122. package/lib-esm/model/earth/XYZLayer.js +0 -231
  123. package/lib-esm/model/earth/XYZTileProvider.d.ts +0 -29
  124. package/lib-esm/model/earth/XYZTileProvider.js +0 -187
  125. package/lib-esm/model/earth/wmts/types.d.ts +0 -1
  126. package/lib-esm/model/earth/wmts/types.js +0 -1
  127. /package/lib-esm/model/{earth/types.js → hips/HiPSConfig.js} +0 -0
@@ -1,39 +1,37 @@
1
- import { XYZShaderProgram } from '../../shader/XYZShaderProgram.js';
2
- import { XYZMeshBuilder } from './XYZMeshBuilder.js';
3
- import { XYZTile } from './XYZTile.js';
4
- import type { XYZTileCoord } from './types.js';
5
- export type XYZTileRequest = {
6
- tileCoord: XYZTileCoord;
7
- url: string;
8
- priority: number;
9
- role: 'current' | 'coverage' | 'fallback' | 'base';
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;
10
9
  };
11
- type XYZTileBufferEntry = {
12
- tile: XYZTile;
10
+ export type XYZTileBufferEntry<TTile extends XYZBufferedTile = XYZBufferedTile> = {
11
+ tile: TTile;
13
12
  cacheTime0?: number;
14
- role: 'current' | 'coverage' | 'fallback' | 'base';
15
13
  };
16
- export declare class XYZTileBuffer {
14
+ export type XYZTileFactory<TTile extends XYZBufferedTile = XYZBufferedTile> = (coord: XYZTileCoord) => TTile;
15
+ export declare class XYZTileBuffer<TTile extends XYZBufferedTile = XYZBufferedTile> {
17
16
  private _tiles;
18
17
  private _cachedTiles;
19
18
  private _cacheAliveMilliSeconds;
20
19
  private _cleanerId;
21
- private _webgl;
22
- private _meshBuilder;
23
- private _shaderProgram;
24
- constructor(minutesToLiveInCache: number | undefined, webgl: WebGL2RenderingContext, meshBuilder: XYZMeshBuilder, shaderProgram: XYZShaderProgram);
25
- get activeTiles(): Map<string, XYZTileBufferEntry>;
26
- get cachedTiles(): Map<string, XYZTileBufferEntry>;
20
+ constructor(minutesToLiveInCache?: number);
21
+ get activeTiles(): Map<string, XYZTileBufferEntry<TTile>>;
22
+ get cachedTiles(): Map<string, XYZTileBufferEntry<TTile>>;
27
23
  get size(): number;
28
- getTile(tileCoord: XYZTileCoord, url: string, segmentsPerSide: number, role: 'current' | 'coverage' | 'fallback' | 'base'): XYZTile;
29
- private getExistingTile;
30
- ensureTiles(requests: XYZTileRequest[], segmentsPerSide: number): string[];
31
- getActiveTile(tileKey: string): XYZTile | null;
32
- getAnyTile(tileKey: string): XYZTile | null;
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[];
33
29
  syncVisibleTiles(visibleTileKeys: string[]): void;
34
30
  evictCached(maxCachedTiles: number): void;
35
31
  dispose(): void;
32
+ key(tileCoord: XYZTileCoord): string;
33
+ static key(tileCoord: XYZTileCoord): string;
36
34
  private cacheCleaner;
37
- private key;
35
+ private touchTile;
36
+ private getTileAgeScore;
38
37
  }
39
- export {};
@@ -1,20 +1,27 @@
1
- import { XYZTile } from './XYZTile.js';
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
+ */
2
13
  export class XYZTileBuffer {
3
14
  _tiles = new Map();
4
15
  _cachedTiles = new Map();
5
16
  _cacheAliveMilliSeconds;
6
17
  _cleanerId;
7
- _webgl;
8
- _meshBuilder;
9
- _shaderProgram;
10
- constructor(minutesToLiveInCache = 1, webgl, meshBuilder, shaderProgram) {
18
+ constructor(minutesToLiveInCache = 1) {
11
19
  this._cacheAliveMilliSeconds = minutesToLiveInCache * 60 * 1000;
12
- this._webgl = webgl;
13
- this._meshBuilder = meshBuilder;
14
- this._shaderProgram = shaderProgram;
15
- this._cleanerId = window.setInterval(() => {
16
- this.cacheCleaner();
17
- }, 10_000);
20
+ if (typeof window !== 'undefined') {
21
+ this._cleanerId = window.setInterval(() => {
22
+ this.cacheCleaner();
23
+ }, 10_000);
24
+ }
18
25
  }
19
26
  get activeTiles() {
20
27
  return this._tiles;
@@ -25,64 +32,31 @@ export class XYZTileBuffer {
25
32
  get size() {
26
33
  return this._tiles.size + this._cachedTiles.size;
27
34
  }
28
- getTile(tileCoord, url, segmentsPerSide, role) {
29
- const tileKey = this.key(tileCoord);
30
- if (!this._tiles.has(tileKey)) {
31
- if (this._cachedTiles.has(tileKey)) {
32
- const entry = this._cachedTiles.get(tileKey);
33
- entry.cacheTime0 = undefined;
34
- entry.role = role;
35
- this._tiles.set(tileKey, entry);
36
- this._cachedTiles.delete(tileKey);
37
- }
38
- else {
39
- const mesh = this._meshBuilder.buildTileMesh(tileCoord, segmentsPerSide);
40
- this._tiles.set(tileKey, {
41
- tile: new XYZTile(tileCoord, url, mesh, this._webgl, this._shaderProgram),
42
- role,
43
- });
44
- }
45
- }
46
- else {
47
- const entry = this._tiles.get(tileKey);
48
- entry.role = role;
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));
49
41
  }
50
- return this._tiles.get(tileKey).tile;
42
+ this.syncVisibleTiles(visibleTileKeys);
43
+ return visibleTileKeys;
51
44
  }
52
- getExistingTile(tileCoord, role) {
45
+ getTile(tileCoord, tileFactory) {
53
46
  const tileKey = this.key(tileCoord);
54
47
  if (this._tiles.has(tileKey)) {
55
- const entry = this._tiles.get(tileKey);
56
- entry.role = role;
57
- return entry.tile;
48
+ return this._tiles.get(tileKey).tile;
58
49
  }
59
50
  if (this._cachedTiles.has(tileKey)) {
60
51
  const entry = this._cachedTiles.get(tileKey);
61
52
  entry.cacheTime0 = undefined;
62
- entry.role = role;
63
53
  this._tiles.set(tileKey, entry);
64
54
  this._cachedTiles.delete(tileKey);
65
55
  return entry.tile;
66
56
  }
67
- return null;
68
- }
69
- ensureTiles(requests, segmentsPerSide) {
70
- const visibleTileKeys = [];
71
- for (const request of requests) {
72
- const tile = request.role === 'fallback'
73
- ? this.getExistingTile(request.tileCoord, 'fallback')
74
- : this.getTile(request.tileCoord, request.url, segmentsPerSide, request.role);
75
- if (!tile) {
76
- continue;
77
- }
78
- tile.touch();
79
- if (request.role !== 'fallback') {
80
- tile.primeLoad(request.priority);
81
- }
82
- visibleTileKeys.push(this.key(request.tileCoord));
83
- }
84
- this.syncVisibleTiles(visibleTileKeys);
85
- return visibleTileKeys;
57
+ const tile = tileFactory(tileCoord);
58
+ this._tiles.set(tileKey, { tile });
59
+ return tile;
86
60
  }
87
61
  getActiveTile(tileKey) {
88
62
  return this._tiles.get(tileKey)?.tile ?? null;
@@ -90,11 +64,14 @@ export class XYZTileBuffer {
90
64
  getAnyTile(tileKey) {
91
65
  return this._tiles.get(tileKey)?.tile ?? this._cachedTiles.get(tileKey)?.tile ?? null;
92
66
  }
67
+ getActiveTiles() {
68
+ return Array.from(this._tiles.values(), (entry) => entry.tile);
69
+ }
93
70
  syncVisibleTiles(visibleTileKeys) {
94
71
  const visibleKeySet = new Set(visibleTileKeys);
95
72
  for (const [tileKey, entry] of this._tiles) {
96
73
  if (visibleKeySet.has(tileKey)) {
97
- entry.tile.touch();
74
+ this.touchTile(entry.tile);
98
75
  continue;
99
76
  }
100
77
  entry.cacheTime0 = Date.now();
@@ -107,9 +84,7 @@ export class XYZTileBuffer {
107
84
  return;
108
85
  }
109
86
  const candidates = Array.from(this._cachedTiles.entries()).sort((a, b) => {
110
- const scoreA = Math.min(a[1].tile.lastUsedAt || a[1].tile.createdAt, a[1].tile.createdAt);
111
- const scoreB = Math.min(b[1].tile.lastUsedAt || b[1].tile.createdAt, b[1].tile.createdAt);
112
- return scoreA - scoreB;
87
+ return this.getTileAgeScore(a[1].tile) - this.getTileAgeScore(b[1].tile);
113
88
  });
114
89
  for (const [tileKey, entry] of candidates) {
115
90
  if (this.size <= maxCachedTiles) {
@@ -118,32 +93,47 @@ export class XYZTileBuffer {
118
93
  if (entry.tile.loading) {
119
94
  continue;
120
95
  }
121
- entry.tile.dispose();
96
+ entry.tile.dispose?.();
122
97
  this._cachedTiles.delete(tileKey);
123
98
  }
124
99
  }
125
100
  dispose() {
126
- window.clearInterval(this._cleanerId);
101
+ if (this._cleanerId !== undefined && typeof window !== 'undefined') {
102
+ window.clearInterval(this._cleanerId);
103
+ this._cleanerId = undefined;
104
+ }
127
105
  for (const entry of this._tiles.values()) {
128
- entry.tile.dispose();
106
+ entry.tile.dispose?.();
129
107
  }
130
108
  for (const entry of this._cachedTiles.values()) {
131
- entry.tile.dispose();
109
+ entry.tile.dispose?.();
132
110
  }
133
111
  this._tiles.clear();
134
112
  this._cachedTiles.clear();
135
113
  }
114
+ key(tileCoord) {
115
+ return XYZTileBuffer.key(tileCoord);
116
+ }
117
+ static key(tileCoord) {
118
+ return `${tileCoord.z}/${tileCoord.x}/${tileCoord.y}`;
119
+ }
136
120
  cacheCleaner() {
137
121
  const now = Date.now();
138
122
  for (const [tileKey, entry] of this._cachedTiles) {
139
123
  const t0 = entry.cacheTime0;
140
- if (t0 !== undefined && now - t0 > this._cacheAliveMilliSeconds && !entry.tile.loading) {
141
- entry.tile.dispose();
142
- this._cachedTiles.delete(tileKey);
124
+ if (t0 === undefined || now - t0 <= this._cacheAliveMilliSeconds || entry.tile.loading) {
125
+ continue;
143
126
  }
127
+ entry.tile.dispose?.();
128
+ this._cachedTiles.delete(tileKey);
144
129
  }
145
130
  }
146
- key(tileCoord) {
147
- return `${tileCoord.z}/${tileCoord.x}/${tileCoord.y}`;
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);
148
138
  }
149
139
  }
@@ -1,4 +1,4 @@
1
- import type { XYZRequestSchedulerDebugStats } from './types.js';
1
+ import type { XYZRequestSchedulerDebugStats } from './XYZConfig.js';
2
2
  export declare class XYZTileRequestError extends Error {
3
3
  cooldownMs: number;
4
4
  retryable: boolean;
@@ -1,3 +1,15 @@
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
+ */
1
13
  export class XYZTileRequestError extends Error {
2
14
  cooldownMs;
3
15
  retryable;
@@ -73,9 +85,8 @@ export class XYZTileRequestScheduler {
73
85
  pump() {
74
86
  while (this._activeCount < this._maxConcurrent && this._queue.length > 0) {
75
87
  const item = this._queue.shift();
76
- if (!item) {
88
+ if (!item)
77
89
  return;
78
- }
79
90
  const hostCooldown = this.getHostCooldown(item.url);
80
91
  const now = Date.now();
81
92
  if (hostCooldown > now) {
@@ -0,0 +1,24 @@
1
+ export type XYZTileCoord = {
2
+ z: number;
3
+ x: number;
4
+ y: number;
5
+ };
6
+ export type XYZVisibleTileSelection = {
7
+ key: string;
8
+ currentZoom: number;
9
+ visibleTiles: XYZTileCoord[];
10
+ visibleTilesMap: Map<string, XYZTileCoord>;
11
+ ancestorsMap: Map<string, XYZTileCoord>;
12
+ };
13
+ export type XYZTileMesh = {
14
+ positions: Float32Array;
15
+ uvs: Float32Array;
16
+ indices: Uint16Array | Uint32Array;
17
+ };
18
+ export type XYZTileGpuMesh = {
19
+ positionBuffer: WebGLBuffer | null;
20
+ uvBuffer: WebGLBuffer | null;
21
+ indexBuffer: WebGLBuffer | null;
22
+ indexCount: number;
23
+ indexType: number;
24
+ };
@@ -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 {};
@@ -1,30 +1,19 @@
1
- import type { XYZTileCoord } from './types.js';
2
- import { XYZTileProvider } from './XYZTileProvider.js';
3
- import type { SkyEntityDrawInput } from '../AbstractSkyEntity.js';
4
- export type XYZTileSelection = {
5
- key: string;
6
- currentTiles: XYZTileCoord[];
7
- fallbackTiles: XYZTileCoord[];
8
- currentZoom: number;
9
- coreTileCount: number;
10
- coverageTileCount: number;
11
- };
1
+ import { ReadonlyMat4 } from 'gl-matrix';
2
+ import Camera from '../../Camera.js';
3
+ import { AbstractSkyEntity } from '../AbstractSkyEntity.js';
4
+ import type { XYZTileCoord, XYZVisibleTileSelection } from './XYZTypes.js';
12
5
  export declare class XYZVisibleTilesManager {
13
- private _provider;
14
- constructor(provider: XYZTileProvider);
15
- selectTiles(input: SkyEntityDrawInput): XYZTileSelection;
16
- private buildCoreVisibleTiles;
17
- private buildCoverageTiles;
18
- private collectCoverageSamples;
19
- private interpolateFoVPolygon;
20
- private getNeighborRing;
21
- private isBoundaryTile;
22
- private normalizeTile;
23
- private normalizePhi;
24
- private buildFallbackMap;
25
- private buildFallbackSeedTiles;
26
- private orderTilesByScreenRelevance;
27
- private orderFallbackTiles;
28
- private getCenterTileCoord;
6
+ private _ancestorsMap;
7
+ private _visibleTilesMap;
8
+ private _visibleTiles;
9
+ private _selection;
10
+ get ancestorsMap(): Map<string, XYZTileCoord>;
11
+ get visibleTiles(): XYZTileCoord[];
12
+ get visibleTilesMap(): Map<string, XYZTileCoord>;
13
+ get selection(): XYZVisibleTileSelection;
14
+ computeVisibleTiles(z: number, xyzModel: AbstractSkyEntity, webgl: WebGL2RenderingContext, camera: Camera, pMatrix: ReadonlyMat4, sampleCount?: number, padding?: number): XYZVisibleTileSelection;
15
+ private refreshAncestorsMap;
16
+ private buildTileMap;
17
+ private buildSelectionKey;
29
18
  private key;
30
19
  }
@@ -1,206 +1,75 @@
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 XYZRayPickingUtils from '../../utils/XYZRayPickingUtils.js';
1
14
  export class XYZVisibleTilesManager {
2
- _provider;
3
- constructor(provider) {
4
- this._provider = provider;
5
- }
6
- selectTiles(input) {
7
- const currentZoom = this._provider.resolveZoom(input.fovDeg ?? 180);
8
- const coreTiles = this.orderTilesByScreenRelevance(this.buildCoreVisibleTiles(currentZoom, input), currentZoom, input.centerSphericalDeg ?? null);
9
- const coverageTiles = this.orderTilesByScreenRelevance(this.buildCoverageTiles(currentZoom, input, coreTiles), currentZoom, input.centerSphericalDeg ?? null);
10
- const currentTiles = [...coreTiles, ...coverageTiles];
11
- const fallbackSeedTiles = this.buildFallbackSeedTiles(coreTiles, currentZoom, input.centerSphericalDeg ?? null);
12
- const fallbackTiles = this.orderFallbackTiles(Array.from(this.buildFallbackMap(fallbackSeedTiles, currentZoom).values()), currentZoom, input.centerSphericalDeg ?? null);
13
- const key = `${currentZoom}:${currentTiles.map((tile) => `${tile.x}/${tile.y}`).join('|')}`;
14
- return {
15
- key,
16
- currentTiles,
17
- fallbackTiles,
18
- currentZoom,
19
- coreTileCount: coreTiles.length,
20
- coverageTileCount: coverageTiles.length,
15
+ _ancestorsMap = new Map();
16
+ _visibleTilesMap = new Map();
17
+ _visibleTiles = [];
18
+ _selection = {
19
+ key: '0:',
20
+ currentZoom: 0,
21
+ visibleTiles: [],
22
+ visibleTilesMap: new Map(),
23
+ ancestorsMap: new Map(),
24
+ };
25
+ get ancestorsMap() {
26
+ return this._ancestorsMap;
27
+ }
28
+ get visibleTiles() {
29
+ return this._visibleTiles;
30
+ }
31
+ get visibleTilesMap() {
32
+ return this._visibleTilesMap;
33
+ }
34
+ get selection() {
35
+ return this._selection;
36
+ }
37
+ computeVisibleTiles(z, xyzModel, webgl, camera, pMatrix, sampleCount = 9, padding = 2) {
38
+ this._visibleTiles = XYZRayPickingUtils.getVisibleTilesFromViewport(z, xyzModel, webgl, camera, pMatrix, sampleCount, padding);
39
+ this._visibleTilesMap = this.buildTileMap(this._visibleTiles);
40
+ this.refreshAncestorsMap(this._visibleTiles);
41
+ this._selection = {
42
+ key: this.buildSelectionKey(z, this._visibleTiles),
43
+ currentZoom: z,
44
+ visibleTiles: this._visibleTiles,
45
+ visibleTilesMap: this._visibleTilesMap,
46
+ ancestorsMap: this._ancestorsMap,
21
47
  };
22
- }
23
- buildCoreVisibleTiles(currentZoom, input) {
24
- const samples = this.collectCoverageSamples(input);
25
- if (samples.length === 0) {
26
- return this._provider.getVisibleTilesAtZoom(currentZoom, null, [], [], 0);
27
- }
28
- const tiles = [];
29
- for (const sample of samples) {
30
- tiles.push(this._provider.tileFromSpherical(currentZoom, sample));
31
- }
32
- return this._provider.deduplicateTiles(tiles);
33
- }
34
- buildCoverageTiles(currentZoom, input, coreTiles) {
35
- if (coreTiles.length === 0) {
36
- return [];
37
- }
38
- const ring = this.getNeighborRing(currentZoom, input.fovDeg ?? 180);
39
- if (ring <= 0) {
40
- return [];
41
- }
42
- const coreSet = new Set(coreTiles.map((tile) => this.key(tile)));
43
- const coverageTiles = [];
44
- for (const tile of coreTiles) {
45
- if (!this.isBoundaryTile(tile, coreSet)) {
46
- continue;
47
- }
48
- const neighbors = this._provider.getNeighborTiles(tile, ring);
49
- for (const neighbor of neighbors) {
50
- const neighborKey = this.key(neighbor);
51
- if (coreSet.has(neighborKey)) {
52
- continue;
53
- }
54
- coverageTiles.push(neighbor);
55
- }
56
- }
57
- return this._provider.deduplicateTiles(coverageTiles);
58
- }
59
- collectCoverageSamples(input) {
60
- const samples = [];
61
- if (input.centerSphericalDeg) {
62
- samples.push(input.centerSphericalDeg);
63
- }
64
- for (const sample of input.viewportSphericalSamples ?? []) {
65
- samples.push(sample);
66
- }
67
- const polygonSamples = this.interpolateFoVPolygon(input.fovPolygon ?? []);
68
- for (const sample of polygonSamples) {
69
- samples.push(sample);
70
- }
71
- return samples;
72
- }
73
- interpolateFoVPolygon(fovPolygon) {
74
- if (fovPolygon.length === 0) {
75
- return [];
76
- }
77
- const polygonSpherical = fovPolygon.map((point) => ({
78
- phi: point.raDeg < 0 ? point.raDeg + 360 : point.raDeg,
79
- theta: 90 - point.decDeg,
80
- }));
81
- const samples = [...polygonSpherical];
82
- const segmentInterpolationCount = polygonSpherical.length >= 4 ? 3 : 1;
83
- for (let i = 0; i < polygonSpherical.length; i++) {
84
- const start = polygonSpherical[i];
85
- const end = polygonSpherical[(i + 1) % polygonSpherical.length];
86
- if (!start || !end)
87
- continue;
88
- const startPhi = this.normalizePhi(start.phi);
89
- let endPhi = this.normalizePhi(end.phi);
90
- if (Math.abs(endPhi - startPhi) > 180) {
91
- endPhi += endPhi > startPhi ? -360 : 360;
92
- }
93
- for (let step = 1; step <= segmentInterpolationCount; step++) {
94
- const t = step / (segmentInterpolationCount + 1);
95
- const phi = this.normalizePhi(startPhi + (endPhi - startPhi) * t);
96
- const theta = start.theta + (end.theta - start.theta) * t;
97
- samples.push({ phi, theta });
98
- }
99
- }
100
- return samples;
101
- }
102
- getNeighborRing(currentZoom, fovDeg) {
103
- if (currentZoom >= 14 && fovDeg < 10) {
104
- return 0;
105
- }
106
- return 1;
107
- }
108
- isBoundaryTile(tile, coreTileKeys) {
109
- const directNeighbors = [
110
- { z: tile.z, x: tile.x - 1, y: tile.y },
111
- { z: tile.z, x: tile.x + 1, y: tile.y },
112
- { z: tile.z, x: tile.x, y: tile.y - 1 },
113
- { z: tile.z, x: tile.x, y: tile.y + 1 },
114
- ];
115
- return directNeighbors.some((neighbor) => !coreTileKeys.has(this.key(this.normalizeTile(neighbor))));
116
- }
117
- normalizeTile(tile) {
118
- const dim = 2 ** tile.z;
119
- return {
120
- z: tile.z,
121
- x: ((tile.x % dim) + dim) % dim,
122
- y: Math.max(0, Math.min(dim - 1, tile.y)),
123
- };
124
- }
125
- normalizePhi(phi) {
126
- let value = phi;
127
- while (value < 0)
128
- value += 360;
129
- while (value >= 360)
130
- value -= 360;
131
- return value;
132
- }
133
- buildFallbackMap(currentTiles, currentZoom) {
134
- const fallbackMap = new Map();
135
- const minFallbackZoom = Math.max(this._provider.minZoom, currentZoom - 2);
136
- for (let z = currentZoom - 1; z >= minFallbackZoom; z--) {
137
- const dz = currentZoom - z;
138
- for (const tile of currentTiles) {
139
- const fallback = {
48
+ return this._selection;
49
+ }
50
+ refreshAncestorsMap(visibleTiles) {
51
+ this._ancestorsMap.clear();
52
+ for (const tile of visibleTiles) {
53
+ for (let z = tile.z - 1; z >= 0; z--) {
54
+ const dz = tile.z - z;
55
+ const ancestor = {
140
56
  z,
141
57
  x: tile.x >> dz,
142
58
  y: tile.y >> dz,
143
59
  };
144
- fallbackMap.set(`${fallback.z}/${fallback.x}/${fallback.y}`, fallback);
60
+ this._ancestorsMap.set(`${ancestor.z}/${ancestor.x}/${ancestor.y}`, ancestor);
145
61
  }
146
62
  }
147
- return fallbackMap;
148
63
  }
149
- buildFallbackSeedTiles(coreTiles, zoom, centerSphericalDeg) {
150
- if (coreTiles.length === 0) {
151
- return [];
152
- }
153
- const centerTile = this.getCenterTileCoord(zoom, centerSphericalDeg);
154
- if (!centerTile) {
155
- return coreTiles;
64
+ buildTileMap(tiles) {
65
+ const map = new Map();
66
+ for (const tile of tiles) {
67
+ map.set(this.key(tile), tile);
156
68
  }
157
- const centerKey = this.key(centerTile);
158
- if (coreTiles.some((tile) => this.key(tile) === centerKey)) {
159
- return coreTiles;
160
- }
161
- return [...coreTiles, centerTile];
162
- }
163
- orderTilesByScreenRelevance(tiles, zoom, centerSphericalDeg) {
164
- const centerTile = this.getCenterTileCoord(zoom, centerSphericalDeg);
165
- if (!centerTile) {
166
- return tiles;
167
- }
168
- return [...tiles].sort((a, b) => {
169
- const distanceA = Math.abs(a.x - centerTile.x) + Math.abs(a.y - centerTile.y);
170
- const distanceB = Math.abs(b.x - centerTile.x) + Math.abs(b.y - centerTile.y);
171
- return distanceA - distanceB;
172
- });
173
- }
174
- orderFallbackTiles(tiles, currentZoom, centerSphericalDeg) {
175
- return [...tiles].sort((a, b) => {
176
- if (b.z !== a.z) {
177
- return b.z - a.z;
178
- }
179
- const centerTileA = this.getCenterTileCoord(a.z, centerSphericalDeg);
180
- const centerTileB = this.getCenterTileCoord(b.z, centerSphericalDeg);
181
- if (!centerTileA || !centerTileB) {
182
- return 0;
183
- }
184
- const distanceA = Math.abs(a.x - centerTileA.x) + Math.abs(a.y - centerTileA.y);
185
- const distanceB = Math.abs(b.x - centerTileB.x) + Math.abs(b.y - centerTileB.y);
186
- return distanceA - distanceB || currentZoom - a.z - (currentZoom - b.z);
187
- });
69
+ return map;
188
70
  }
189
- getCenterTileCoord(zoom, centerSphericalDeg) {
190
- if (!centerSphericalDeg) {
191
- return null;
192
- }
193
- const lonDeg = centerSphericalDeg.phi > 180 ? centerSphericalDeg.phi - 360 : centerSphericalDeg.phi;
194
- const latDeg = 90 - centerSphericalDeg.theta;
195
- const dim = 2 ** zoom;
196
- const x = Math.floor(((lonDeg + 180) / 360) * dim);
197
- const latRad = (Math.max(-85.0511287798066, Math.min(85.0511287798066, latDeg)) * Math.PI) / 180;
198
- const y = Math.floor(((1 - Math.asinh(Math.tan(latRad)) / Math.PI) / 2) * dim);
199
- return {
200
- z: zoom,
201
- x: ((x % dim) + dim) % dim,
202
- y: Math.max(0, Math.min(dim - 1, y)),
203
- };
71
+ buildSelectionKey(z, tiles) {
72
+ return `${z}:${tiles.map((tile) => `${tile.x}/${tile.y}`).join('|')}`;
204
73
  }
205
74
  key(tile) {
206
75
  return `${tile.z}/${tile.x}/${tile.y}`;