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,172 @@
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 XYZTileRequestError extends Error {
14
+ cooldownMs;
15
+ retryable;
16
+ constructor(message, cooldownMs, retryable = true) {
17
+ super(message);
18
+ this.name = 'XYZTileRequestError';
19
+ this.cooldownMs = cooldownMs;
20
+ this.retryable = retryable;
21
+ }
22
+ }
23
+ export class XYZTileRequestScheduler {
24
+ _maxConcurrent;
25
+ _activeCount = 0;
26
+ _queue = [];
27
+ _inflight = new Map();
28
+ _hostBackoff = new Map();
29
+ _sequence = 0;
30
+ constructor(maxConcurrent = 4) {
31
+ this._maxConcurrent = maxConcurrent;
32
+ }
33
+ setMaxConcurrent(maxConcurrent) {
34
+ this._maxConcurrent = Math.max(1, Math.floor(maxConcurrent));
35
+ this.pump();
36
+ }
37
+ getMaxConcurrent() {
38
+ return this._maxConcurrent;
39
+ }
40
+ getDebugStats() {
41
+ const now = Date.now();
42
+ const hostsInBackoff = Array.from(this._hostBackoff.entries())
43
+ .map(([host, state]) => ({
44
+ host,
45
+ cooldownMs: Math.max(0, state.cooldownUntil - now),
46
+ consecutiveFailures: state.consecutiveFailures,
47
+ }))
48
+ .filter((entry) => entry.cooldownMs > 0 || entry.consecutiveFailures > 0)
49
+ .sort((a, b) => b.cooldownMs - a.cooldownMs);
50
+ return {
51
+ activeRequests: this._activeCount,
52
+ queuedRequests: this._queue.length,
53
+ inflightRequests: this._inflight.size,
54
+ maxConcurrentRequests: this._maxConcurrent,
55
+ highestQueuedPriority: this._queue[0]?.priority ?? null,
56
+ hostsInBackoff,
57
+ };
58
+ }
59
+ load(url, priority = 0) {
60
+ const inflight = this._inflight.get(url);
61
+ if (inflight) {
62
+ return inflight;
63
+ }
64
+ const hostCooldown = this.getHostCooldown(url);
65
+ const now = Date.now();
66
+ if (hostCooldown > now) {
67
+ return Promise.reject(new XYZTileRequestError(`Cooldown active for ${new URL(url).host}`, hostCooldown - now));
68
+ }
69
+ const promise = new Promise((resolve, reject) => {
70
+ this._queue.push({
71
+ url,
72
+ resolve,
73
+ reject,
74
+ priority,
75
+ sequence: this._sequence++,
76
+ });
77
+ this._queue.sort((a, b) => b.priority - a.priority || a.sequence - b.sequence);
78
+ this.pump();
79
+ }).finally(() => {
80
+ this._inflight.delete(url);
81
+ });
82
+ this._inflight.set(url, promise);
83
+ return promise;
84
+ }
85
+ pump() {
86
+ while (this._activeCount < this._maxConcurrent && this._queue.length > 0) {
87
+ const item = this._queue.shift();
88
+ if (!item)
89
+ return;
90
+ const hostCooldown = this.getHostCooldown(item.url);
91
+ const now = Date.now();
92
+ if (hostCooldown > now) {
93
+ item.reject(new XYZTileRequestError(`Cooldown active for ${new URL(item.url).host}`, hostCooldown - now));
94
+ continue;
95
+ }
96
+ this._activeCount += 1;
97
+ this.fetchBlob(item)
98
+ .then(item.resolve)
99
+ .catch(item.reject)
100
+ .finally(() => {
101
+ this._activeCount -= 1;
102
+ this.pump();
103
+ });
104
+ }
105
+ }
106
+ async fetchBlob(item) {
107
+ try {
108
+ const response = await fetch(item.url, {
109
+ mode: 'cors',
110
+ cache: 'force-cache',
111
+ });
112
+ if (!response.ok) {
113
+ const isTransient = response.status === 429 || response.status >= 500;
114
+ const cooldownMs = this.registerFailure(item.url, isTransient);
115
+ throw new XYZTileRequestError(`HTTP ${response.status} loading ${item.url}`, isTransient ? cooldownMs : 5 * 60 * 1000, isTransient);
116
+ }
117
+ this.registerSuccess(item.url);
118
+ return await response.blob();
119
+ }
120
+ catch (error) {
121
+ if (error instanceof XYZTileRequestError) {
122
+ throw error;
123
+ }
124
+ const cooldownMs = this.registerFailure(item.url, false);
125
+ throw new XYZTileRequestError(`Network/CORS failure loading ${item.url}`, cooldownMs, true);
126
+ }
127
+ }
128
+ getHostCooldown(url) {
129
+ try {
130
+ return this._hostBackoff.get(new URL(url).host)?.cooldownUntil ?? 0;
131
+ }
132
+ catch {
133
+ return 0;
134
+ }
135
+ }
136
+ registerSuccess(url) {
137
+ try {
138
+ const host = new URL(url).host;
139
+ this._hostBackoff.set(host, {
140
+ cooldownUntil: 0,
141
+ consecutiveFailures: 0,
142
+ });
143
+ }
144
+ catch {
145
+ // no-op
146
+ }
147
+ }
148
+ registerFailure(url, isRateLimited) {
149
+ if (!isRateLimited) {
150
+ return 0;
151
+ }
152
+ try {
153
+ const host = new URL(url).host;
154
+ const previous = this._hostBackoff.get(host) ?? {
155
+ cooldownUntil: 0,
156
+ consecutiveFailures: 0,
157
+ };
158
+ const consecutiveFailures = previous.consecutiveFailures + 1;
159
+ const baseDelayMs = 4000;
160
+ const cooldownMs = Math.min(120000, baseDelayMs * (2 ** Math.min(consecutiveFailures - 1, 5)));
161
+ this._hostBackoff.set(host, {
162
+ cooldownUntil: Date.now() + cooldownMs,
163
+ consecutiveFailures,
164
+ });
165
+ return cooldownMs;
166
+ }
167
+ catch {
168
+ return 30000;
169
+ }
170
+ }
171
+ }
172
+ export const xyzTileRequestScheduler = new XYZTileRequestScheduler();
@@ -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 {};
@@ -0,0 +1,19 @@
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';
5
+ export declare class XYZVisibleTilesManager {
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;
18
+ private key;
19
+ }
@@ -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
+ import XYZRayPickingUtils from '../../utils/XYZRayPickingUtils.js';
14
+ export class XYZVisibleTilesManager {
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,
47
+ };
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 = {
56
+ z,
57
+ x: tile.x >> dz,
58
+ y: tile.y >> dz,
59
+ };
60
+ this._ancestorsMap.set(`${ancestor.z}/${ancestor.x}/${ancestor.y}`, ancestor);
61
+ }
62
+ }
63
+ }
64
+ buildTileMap(tiles) {
65
+ const map = new Map();
66
+ for (const tile of tiles) {
67
+ map.set(this.key(tile), tile);
68
+ }
69
+ return map;
70
+ }
71
+ buildSelectionKey(z, tiles) {
72
+ return `${z}:${tiles.map((tile) => `${tile.x}/${tile.y}`).join('|')}`;
73
+ }
74
+ key(tile) {
75
+ return `${tile.z}/${tile.x}/${tile.y}`;
76
+ }
77
+ }
@@ -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
  'use strict';
2
14
  /**
3
15
  * @author Fabrizio Giordano (Fab)
@@ -1,6 +1,18 @@
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
14
  // import {Metadata} from '../tap/TapMetadata.js';
2
15
  // import {TapMetadataList} from '../tap/TapMetadataList.js';
3
- export {};
4
16
  // export default class FootprintProps {
5
17
  // // resolved columns
6
18
  // pgSphereColumn?: Metadata;
@@ -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
  import { Footprint } from "./Footprint.js";
2
14
  import { colorHex2RGB } from "../../utils/Utils.js";
3
15
  import { FootprintShaderProgram } from "../../shader/FootprintShaderProgram.js";
@@ -126,7 +138,7 @@ export class FootprintSetGL {
126
138
  throw new Error("geomColumn or its index is undefined in footprintsetProps");
127
139
  }
128
140
  for (let j = 0; j < in_data.length; j++) {
129
- if (in_data[j][0] !== null) {
141
+ if (in_data[j][geomDataIndex] !== null) {
130
142
  const footprint = new Footprint(in_data[j][geomDataIndex], in_data[j]);
131
143
  if (footprint._valid) {
132
144
  this.addFootprint(footprint);
@@ -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
  /* eslint-disable @typescript-eslint/no-non-null-assertion */
2
14
  import { vec4, mat4 } from 'gl-matrix';
3
15
  import { fovHelper } from '../hips/FoVHelper.js';
@@ -19,7 +31,7 @@ export class EquatorialGrid extends AbstractSkyEntity {
19
31
  _vertexShader;
20
32
  _fragmentShader;
21
33
  defaultColor = '#41d421';
22
- gridText = new GridTextHelper();
34
+ gridText = new GridTextHelper('equatorial');
23
35
  _attribLocations = {
24
36
  position: 0,
25
37
  selected: 1,
@@ -260,9 +272,10 @@ export class EquatorialGrid extends AbstractSkyEntity {
260
272
  // perspective divide
261
273
  clipspace[0] /= clipspace[3];
262
274
  clipspace[1] /= clipspace[3];
263
- // clip->pixel
264
- const pixelX = (clipspace[0] * 0.5 + 0.5) * super.webgl.canvas.width;
265
- const pixelY = (clipspace[1] * -0.5 + 0.5) * super.webgl.canvas.height;
275
+ // clip -> CSS pixels
276
+ const canvasRect = super.webgl.canvas.getBoundingClientRect();
277
+ const pixelX = canvasRect.left + (clipspace[0] * 0.5 + 0.5) * canvasRect.width;
278
+ const pixelY = canvasRect.top + (clipspace[1] * -0.5 + 0.5) * canvasRect.height;
266
279
  this.gridText.addEqDivSet(decDeg.toFixed(2), pixelX, pixelY, 'dec');
267
280
  // gridTextHelper.addEqDivSet(decDeg.toFixed(2), pixelX, pixelY, 'dec');
268
281
  }
@@ -283,8 +296,9 @@ export class EquatorialGrid extends AbstractSkyEntity {
283
296
  vec4.transformMat4(clipspace, phiPoint, mvpMatrix);
284
297
  clipspace[0] /= clipspace[3];
285
298
  clipspace[1] /= clipspace[3];
286
- const pixelX = (clipspace[0] * 0.5 + 0.5) * super.webgl.canvas.width;
287
- const pixelY = (clipspace[1] * -0.5 + 0.5) * super.webgl.canvas.height;
299
+ const canvasRect = super.webgl.canvas.getBoundingClientRect();
300
+ const pixelX = canvasRect.left + (clipspace[0] * 0.5 + 0.5) * canvasRect.width;
301
+ const pixelY = canvasRect.top + (clipspace[1] * -0.5 + 0.5) * canvasRect.height;
288
302
  // gridTextHelper.addEqDivSet(raDeg.toFixed(2), pixelX, pixelY, 'ra');
289
303
  this.gridText.addEqDivSet(raDeg.toFixed(2), pixelX, pixelY, 'ra');
290
304
  }
@@ -1,25 +1,16 @@
1
- /**
2
- * @author Fabrizio Giordano (Fab)
3
- * @param in_radius - number
4
- * @param in_gl - GL context
5
- * @param in_position - array of double e.g. [0.0, 0.0, 0.0]
6
- */
1
+ type GridLabelLayer = 'healpix' | 'equatorial' | 'lonlat';
7
2
  declare class GridTextHelper {
8
- private _divEqContainerElement;
9
- private _divHPXContainerElement;
10
- private _divSets;
11
- private _divSetNdx;
12
- constructor();
3
+ private static layers;
4
+ private layer;
5
+ constructor(layer?: GridLabelLayer);
13
6
  initHtml(): void;
14
- resetDivSets(): void;
15
- /**
16
- * Add / reuse a floating label for HPX coordinates
17
- */
7
+ resetDivSets(layer?: GridLabelLayer): void;
18
8
  addHPXDivSet(msg: string, x: number, y: number): void;
19
- /**
20
- * Add / reuse a floating label for Equatorial coords
21
- * @param type 'ra' or 'dec'
22
- */
23
9
  addEqDivSet(msg: string, x: number, y: number, type: 'ra' | 'dec'): void;
10
+ addLonLatDivSet(msg: string, x: number, y: number, type: 'lon' | 'lat'): void;
11
+ private addLabel;
12
+ private classNameForKind;
13
+ private static getLayerState;
14
+ private static resolveContainer;
24
15
  }
25
16
  export default GridTextHelper;
@@ -1,91 +1,94 @@
1
- /**
2
- * @author Fabrizio Giordano (Fab)
3
- * @param in_radius - number
4
- * @param in_gl - GL context
5
- * @param in_position - array of double e.g. [0.0, 0.0, 0.0]
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.
6
12
  */
7
13
  class GridTextHelper {
8
- _divEqContainerElement;
9
- _divHPXContainerElement;
10
- _divSets;
11
- _divSetNdx;
12
- constructor() {
13
- this._divEqContainerElement = document.querySelector('#gridcoords');
14
- this._divHPXContainerElement = document.querySelector('#gridhpx');
15
- this._divSetNdx = 0;
16
- this._divSets = [];
14
+ static layers = new Map();
15
+ layer;
16
+ constructor(layer = 'equatorial') {
17
+ this.layer = layer;
18
+ GridTextHelper.getLayerState(layer);
17
19
  }
18
20
  initHtml() {
19
- // Kept for API parity; nothing required here with current logic.
21
+ GridTextHelper.getLayerState(this.layer);
20
22
  }
21
- resetDivSets() {
22
- // Hide remaining divs and reset index
23
- for (; this._divSetNdx < this._divSets.length; ++this._divSetNdx) {
24
- this._divSets[this._divSetNdx].style.display = 'none';
23
+ resetDivSets(layer = this.layer) {
24
+ const state = GridTextHelper.getLayerState(layer);
25
+ for (; state.divSetNdx < state.divSets.length; ++state.divSetNdx) {
26
+ state.divSets[state.divSetNdx].style.display = 'none';
25
27
  }
26
- this._divSetNdx = 0;
28
+ state.divSetNdx = 0;
27
29
  }
28
- /**
29
- * Add / reuse a floating label for HPX coordinates
30
- */
31
30
  addHPXDivSet(msg, x, y) {
32
- let divSet = this._divSets[this._divSetNdx++];
33
- // Create on demand
31
+ this.addLabel('healpix', msg, x + 25, y, 'hpx');
32
+ }
33
+ addEqDivSet(msg, x, y, type) {
34
+ this.addLabel('equatorial', msg, type === 'ra' ? x + 25 : x, type === 'ra' ? y : y + 25, type);
35
+ }
36
+ addLonLatDivSet(msg, x, y, type) {
37
+ this.addLabel('lonlat', msg, type === 'lon' ? x + 25 : x, type === 'lon' ? y : y + 25, type);
38
+ }
39
+ addLabel(layer, msg, x, y, kind) {
40
+ const state = GridTextHelper.getLayerState(layer);
41
+ if (!state.container)
42
+ return;
43
+ let divSet = state.divSets[state.divSetNdx++];
34
44
  if (!divSet) {
35
45
  const div = document.createElement('div');
36
46
  const textNode = document.createTextNode('');
37
- div.className = 'floating-div-ra'; // style like RA tags
38
47
  div.appendChild(textNode);
39
- if (!this._divHPXContainerElement) {
40
- this._divHPXContainerElement = document.querySelector('#gridhpx');
41
- }
42
- if (!this._divHPXContainerElement) {
43
- // If container is still missing, abort gracefully
44
- return;
45
- }
46
- this._divHPXContainerElement.appendChild(div);
48
+ state.container.appendChild(div);
47
49
  divSet = { div, textNode, style: div.style };
48
- this._divSets.push(divSet);
50
+ state.divSets.push(divSet);
49
51
  }
50
- // Show & position
52
+ divSet.div.className = this.classNameForKind(kind);
51
53
  divSet.style.display = 'block';
52
- divSet.style.left = `${Math.floor(x + 25)}px`;
54
+ divSet.style.left = `${Math.floor(x)}px`;
53
55
  divSet.style.top = `${Math.floor(y)}px`;
54
56
  divSet.textNode.nodeValue = msg;
55
57
  }
56
- /**
57
- * Add / reuse a floating label for Equatorial coords
58
- * @param type 'ra' or 'dec'
59
- */
60
- addEqDivSet(msg, x, y, type) {
61
- let divSet = this._divSets[this._divSetNdx++];
62
- if (!divSet) {
63
- const div = document.createElement('div');
64
- const textNode = document.createTextNode('');
65
- div.className = type === 'ra' ? 'floating-div-ra' : 'floating-div-dec';
66
- div.appendChild(textNode);
67
- if (!this._divEqContainerElement) {
68
- this._divEqContainerElement = document.querySelector('#gridcoords');
69
- }
70
- if (!this._divEqContainerElement) {
71
- // If container is still missing, abort gracefully
72
- return;
73
- }
74
- this._divEqContainerElement.appendChild(div);
75
- divSet = { div, textNode, style: div.style };
76
- this._divSets.push(divSet);
58
+ classNameForKind(kind) {
59
+ switch (kind) {
60
+ case 'dec':
61
+ return 'floating-div-dec';
62
+ case 'lat':
63
+ return 'floating-div-lat';
64
+ case 'lon':
65
+ return 'floating-div-lon';
66
+ case 'hpx':
67
+ case 'ra':
68
+ default:
69
+ return 'floating-div-ra';
77
70
  }
78
- divSet.style.display = 'block';
79
- if (type === 'ra') {
80
- divSet.style.left = `${Math.floor(x + 25)}px`;
81
- divSet.style.top = `${Math.floor(y)}px`;
71
+ }
72
+ static getLayerState(layer) {
73
+ const current = GridTextHelper.layers.get(layer);
74
+ if (current) {
75
+ if (!current.container)
76
+ current.container = GridTextHelper.resolveContainer(layer);
77
+ return current;
82
78
  }
83
- else {
84
- divSet.style.left = `${Math.floor(x)}px`;
85
- divSet.style.top = `${Math.floor(y + 25)}px`;
79
+ const state = {
80
+ container: GridTextHelper.resolveContainer(layer),
81
+ divSets: [],
82
+ divSetNdx: 0,
83
+ };
84
+ GridTextHelper.layers.set(layer, state);
85
+ return state;
86
+ }
87
+ static resolveContainer(layer) {
88
+ if (layer === 'healpix') {
89
+ return document.querySelector('#gridhpx');
86
90
  }
87
- divSet.textNode.nodeValue = msg;
91
+ return document.querySelector('#gridcoords');
88
92
  }
89
93
  }
90
- // export const gridTextHelper = new GridTextHelper();
91
94
  export default GridTextHelper;
@@ -1,6 +1,6 @@
1
1
  import { AbstractSkyEntity, SkyEntityDrawInput } from '../AbstractSkyEntity.js';
2
2
  import { ReadonlyMat4 } from 'gl-matrix';
3
- import { FoV } from '../FoV.js';
3
+ import { SphereFoV } from '../SphereFoV.js';
4
4
  import { VisibleTilesManager } from '../hips/VisibleTilesManager.js';
5
5
  import Camera from '../../Camera.js';
6
6
  export declare class HealpixGrid extends AbstractSkyEntity {
@@ -28,13 +28,13 @@ export declare class HealpixGrid extends AbstractSkyEntity {
28
28
  private _visibleTilesManager;
29
29
  constructor(webgl: WebGL2RenderingContext);
30
30
  init(): void;
31
- get fovObj(): FoV;
31
+ get fovObj(): SphereFoV;
32
32
  get RADIUS(): number;
33
33
  get INITIAL_POSITION(): [number, number, number];
34
34
  get INITIAL_PhiRad(): number;
35
35
  get INITIAL_ThetaRad(): number;
36
- refreshFoV(camera: Camera, pMatrix: ReadonlyMat4): FoV;
37
- getFoV(): FoV;
36
+ refreshFoV(camera: Camera, pMatrix: ReadonlyMat4): SphereFoV;
37
+ getFoV(): SphereFoV;
38
38
  getMinFoV(): number;
39
39
  private initShaders;
40
40
  initBuffers(pixels: number[], order: number): void;