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
@@ -2,13 +2,16 @@ import Camera from './Camera.js';
2
2
  import { AstroCoords, HMS, SphericalCoords, DMS } from './utils/Utils.js';
3
3
  import { HiPS } from './model/hips/HiPS.js';
4
4
  import { HiPSDescriptor } from './model/hips/HiPSDescriptor.js';
5
- import { FoV } from './model/FoV.js';
5
+ import { SphereFoV } from './model/SphereFoV.js';
6
6
  import { Point } from './model/Point.js';
7
7
  import { CatalogueGL } from './model/catalogues/CatalogueGL.js';
8
8
  import { FootprintSetGL, HoveredFootprintDetail } from './model/footprints/FootprintSetGL.js';
9
9
  import { EquatorialGrid } from './model/grid/EquatorialGrid.js';
10
10
  import { HealpixGrid } from './model/grid/HealpixGrid.js';
11
11
  import { ColorMap } from './model/ColorMaps.js';
12
+ import type { WMTSLayerConfig, XYZDebugStats, XYZLayerConfig } from './model/earth/XYZConfig.js';
13
+ import { XYZMapDescriptor } from './model/earth/XYZMapDescriptor.js';
14
+ import { XYZMap } from './model/earth/XYZMap.js';
12
15
  export type PointCoordinates = {
13
16
  astroDeg: AstroCoords;
14
17
  raHMS: HMS;
@@ -54,6 +57,8 @@ declare class AstroSphere {
54
57
  private pointerDownY;
55
58
  private pointerDownAt;
56
59
  private _activeHiPS;
60
+ private _activeXYZ2;
61
+ private _activeBaseLayer;
57
62
  private startup;
58
63
  private fov;
59
64
  private activeCatalogues;
@@ -64,6 +69,9 @@ declare class AstroSphere {
64
69
  private lastHoveredSource;
65
70
  private lastHoveredCatalogue;
66
71
  private zoomSensitivity;
72
+ private lockedEastWestRaDeg;
73
+ private lockedNorthSouthDecDeg;
74
+ private keepCameraNorthUp;
67
75
  constructor(canvas: HTMLCanvasElement, webgl: WebGL2RenderingContext);
68
76
  private initCamera;
69
77
  setCamera(camera: Camera): void;
@@ -80,17 +88,30 @@ declare class AstroSphere {
80
88
  private computeZoomStep;
81
89
  setZoomSensitivity(value: number): void;
82
90
  getZoomSensitivity(): number;
91
+ private filterRotationDeltaByAstroLocks;
92
+ private projectModelDirectionToScreen;
93
+ private projectModelPointToScreen;
94
+ private enforceAstronomicalRotationLocks;
95
+ private enforceCameraNorthUp;
83
96
  private emitCameraChanged;
84
97
  private addEventListeners;
85
98
  getPhiThetaDeg(canvas: HTMLCanvasElement): SphericalCoords;
99
+ private collectViewportSphericalSamples;
86
100
  activateHiPS(hipsDescriptor: HiPSDescriptor): void;
101
+ activateXYZ(config: XYZLayerConfig): void;
102
+ activateXYZ2(config: XYZMapDescriptor): void;
103
+ activateWMTS(config: WMTSLayerConfig): void;
87
104
  showCatalogue(cat: CatalogueGL): Promise<CatalogueGL>;
88
105
  deleteCatalogue(catalogue: CatalogueGL): void;
89
106
  showFootprintSet(fset: FootprintSetGL): Promise<FootprintSetGL>;
90
107
  deleteFootprintSet(footprintSet: FootprintSetGL): void;
91
108
  getHoveredFootprints(): HoveredFootprintDetail[];
92
109
  goTo(raDeg: number, decDeg: number): void;
93
- getFoV(): FoV;
110
+ getActiveCoordinateMode(): 'equatorial' | 'galactic' | 'lonlat';
111
+ resetAxesOrientation(): void;
112
+ setKeepCameraNorthUp(enabled: boolean): void;
113
+ isKeepCameraNorthUp(): boolean;
114
+ getFoV(): SphereFoV;
94
115
  getFoVPolygon(): Point[];
95
116
  changeFoV(deg: number): void;
96
117
  changeFoV2(deg: number): void;
@@ -107,6 +128,14 @@ declare class AstroSphere {
107
128
  private prevCentralRaDeg;
108
129
  private prevCentralDecDeg;
109
130
  get activeHiPS(): HiPS | null;
131
+ get activeXYZ(): XYZMap | null;
132
+ isLonLatGridVisible(): boolean;
133
+ toggleLonLatGrid(): boolean;
134
+ setEastWestRotationLocked(locked: boolean): void;
135
+ isEastWestRotationLocked(): boolean;
136
+ setNorthSouthRotationLocked(locked: boolean): void;
137
+ isNorthSouthRotationLocked(): boolean;
138
+ getXYZDebugStats(): XYZDebugStats;
110
139
  draw(canvas: HTMLCanvasElement): void;
111
140
  private emitHoveredSourceIfChanged;
112
141
  }
@@ -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 { bootSetup } from './Config.js';
2
14
  import Camera from './Camera.js';
3
15
  import RayPickingUtils from './utils/RayPickingUtils.js';
@@ -12,6 +24,11 @@ import { EquatorialGrid } from './model/grid/EquatorialGrid.js';
12
24
  import { HealpixGrid } from './model/grid/HealpixGrid.js';
13
25
  import { CoordsType } from './utils/CoordsType.js';
14
26
  import ColorMaps from './model/ColorMaps.js';
27
+ import { xyzTileRequestScheduler } from './model/earth/XYZTileRequestScheduler.js';
28
+ import { WMTSAdapter } from './model/earth/WMTSAdapter.js';
29
+ import { XYZMapDescriptor } from './model/earth/XYZMapDescriptor.js';
30
+ import { XYZMap } from './model/earth/XYZMap.js';
31
+ import { mat4, vec3, vec4 } from 'gl-matrix';
15
32
  /**
16
33
  * AstroSphere — main WebGL scene controller (TS port)
17
34
  */
@@ -36,6 +53,8 @@ class AstroSphere {
36
53
  pointerDownY = null;
37
54
  pointerDownAt = 0;
38
55
  _activeHiPS = null;
56
+ _activeXYZ2 = null;
57
+ _activeBaseLayer = null;
39
58
  startup = true;
40
59
  fov;
41
60
  activeCatalogues = [];
@@ -46,6 +65,9 @@ class AstroSphere {
46
65
  lastHoveredSource = null;
47
66
  lastHoveredCatalogue = null;
48
67
  zoomSensitivity = 1.0;
68
+ lockedEastWestRaDeg = null;
69
+ lockedNorthSouthDecDeg = null;
70
+ keepCameraNorthUp = false;
49
71
  constructor(canvas, webgl) {
50
72
  console.log('[AstroSphere] new instance for canvas', canvas.id);
51
73
  // Keep global GL context (as in original JS)
@@ -145,6 +167,118 @@ class AstroSphere {
145
167
  getZoomSensitivity() {
146
168
  return this.zoomSensitivity;
147
169
  }
170
+ filterRotationDeltaByAstroLocks(deltaX, deltaY) {
171
+ const lockEastWest = this._camera.isRotationLockedY();
172
+ const lockNorthSouth = this._camera.isRotationLockedX();
173
+ if (!lockEastWest && !lockNorthSouth) {
174
+ return { deltaX, deltaY };
175
+ }
176
+ const center = RayPickingUtils.getIntersectionPointWithSingleModel(this.canvas.clientWidth / 2, this.canvas.clientHeight / 2, this._healpixGrid, this._webgl, this._camera, this._perspectiveMatrixManager.pMatrix);
177
+ if (!center || center.length < 3) {
178
+ return { deltaX, deltaY };
179
+ }
180
+ const centerVec = vec3.normalize(vec3.create(), vec3.fromValues(center[0], center[1], center[2]));
181
+ const northAxis = vec3.fromValues(0, 0, 1);
182
+ const eastVec = vec3.cross(vec3.create(), northAxis, centerVec);
183
+ if (vec3.length(eastVec) < 1e-6) {
184
+ vec3.set(eastVec, 1, 0, 0);
185
+ }
186
+ else {
187
+ vec3.normalize(eastVec, eastVec);
188
+ }
189
+ const northProjection = vec3.scale(vec3.create(), centerVec, vec3.dot(northAxis, centerVec));
190
+ const northVec = vec3.subtract(vec3.create(), northAxis, northProjection);
191
+ if (vec3.length(northVec) < 1e-6) {
192
+ vec3.cross(northVec, centerVec, eastVec);
193
+ }
194
+ vec3.normalize(northVec, northVec);
195
+ const eastScreen = this.projectModelDirectionToScreen(centerVec, eastVec);
196
+ const northScreen = this.projectModelDirectionToScreen(centerVec, northVec);
197
+ if (!eastScreen || !northScreen) {
198
+ return { deltaX, deltaY };
199
+ }
200
+ let nextDeltaX = deltaX;
201
+ let nextDeltaY = deltaY;
202
+ if (lockEastWest) {
203
+ const amount = nextDeltaX * eastScreen.x + nextDeltaY * eastScreen.y;
204
+ nextDeltaX -= amount * eastScreen.x;
205
+ nextDeltaY -= amount * eastScreen.y;
206
+ }
207
+ if (lockNorthSouth) {
208
+ const amount = nextDeltaX * northScreen.x + nextDeltaY * northScreen.y;
209
+ nextDeltaX -= amount * northScreen.x;
210
+ nextDeltaY -= amount * northScreen.y;
211
+ }
212
+ return {
213
+ deltaX: nextDeltaX,
214
+ deltaY: nextDeltaY,
215
+ };
216
+ }
217
+ projectModelDirectionToScreen(centerModel, directionModel) {
218
+ const offsetModel = vec3.scaleAndAdd(vec3.create(), centerModel, directionModel, 0.01);
219
+ const centerScreen = this.projectModelPointToScreen(centerModel);
220
+ const offsetScreen = this.projectModelPointToScreen(offsetModel);
221
+ if (!centerScreen || !offsetScreen) {
222
+ return null;
223
+ }
224
+ const x = offsetScreen.x - centerScreen.x;
225
+ const y = offsetScreen.y - centerScreen.y;
226
+ const len = Math.hypot(x, y);
227
+ if (len < 1e-6) {
228
+ return null;
229
+ }
230
+ return { x: x / len, y: y / len };
231
+ }
232
+ projectModelPointToScreen(pointModel) {
233
+ const vMatrix = this._camera.getCameraMatrix();
234
+ const mMatrix = this._healpixGrid.getModelMatrix();
235
+ const mvMatrix = mat4.create();
236
+ const mvpMatrix = mat4.create();
237
+ mat4.multiply(mvMatrix, vMatrix, mMatrix);
238
+ mat4.multiply(mvpMatrix, this._perspectiveMatrixManager.pMatrix, mvMatrix);
239
+ const clip = vec4.fromValues(pointModel[0], pointModel[1], pointModel[2], 1);
240
+ vec4.transformMat4(clip, clip, mvpMatrix);
241
+ if (Math.abs(clip[3]) < 1e-6) {
242
+ return null;
243
+ }
244
+ return {
245
+ x: clip[0] / clip[3],
246
+ y: -(clip[1] / clip[3]),
247
+ };
248
+ }
249
+ enforceAstronomicalRotationLocks() {
250
+ if (this.lockedEastWestRaDeg == null && this.lockedNorthSouthDecDeg == null) {
251
+ return false;
252
+ }
253
+ const center = this.updateCentralPoint();
254
+ if (!center) {
255
+ return false;
256
+ }
257
+ const nextRa = this.lockedEastWestRaDeg ?? center.astroDeg.ra;
258
+ const nextDec = this.lockedNorthSouthDecDeg ?? center.astroDeg.dec;
259
+ const needsCorrection = Math.abs(nextRa - center.astroDeg.ra) > 1e-6 ||
260
+ Math.abs(nextDec - center.astroDeg.dec) > 1e-6;
261
+ if (!needsCorrection) {
262
+ return false;
263
+ }
264
+ this._camera.goTo(nextRa, nextDec);
265
+ this._perspectiveMatrixManager.computePerspectiveMatrix(this.canvas, this._camera, bootSetup.camera_fov_deg, bootSetup.camera_near_plane, global.insideSphere);
266
+ this.updateCentralPoint();
267
+ return true;
268
+ }
269
+ enforceCameraNorthUp() {
270
+ if (!this.keepCameraNorthUp) {
271
+ return false;
272
+ }
273
+ const center = this.updateCentralPoint();
274
+ if (!center) {
275
+ return false;
276
+ }
277
+ this._camera.goTo(center.astroDeg.ra, center.astroDeg.dec);
278
+ this._perspectiveMatrixManager.computePerspectiveMatrix(this.canvas, this._camera, bootSetup.camera_fov_deg, bootSetup.camera_near_plane, global.insideSphere);
279
+ this.updateCentralPoint();
280
+ return true;
281
+ }
148
282
  emitCameraChanged(reason) {
149
283
  // avoid dispatch before scene is ready
150
284
  if (!this._activeHiPS)
@@ -261,8 +395,9 @@ class AstroSphere {
261
395
  // Rotation deltas – either use client-space or local-space, but be consistent
262
396
  const deltaX = ((newX - (this.lastMouseX ?? newX)) * Math.PI) / canvas.width;
263
397
  const deltaY = ((newY - (this.lastMouseY ?? newY)) * Math.PI) / canvas.height;
264
- this.inertiaX += 0.1 * deltaX;
265
- this.inertiaY += 0.1 * deltaY;
398
+ const filteredDelta = this.filterRotationDeltaByAstroLocks(deltaX, deltaY);
399
+ this.inertiaX += 0.1 * filteredDelta.deltaX;
400
+ this.inertiaY += 0.1 * filteredDelta.deltaY;
266
401
  this.updateCentralPoint();
267
402
  }
268
403
  else {
@@ -392,8 +527,40 @@ class AstroSphere {
392
527
  const pickerPoint = RayPickingUtils.getIntersectionPointWithSingleModel(maxX / 2, maxY / 2, this._healpixGrid, this._webgl, this._camera, this._perspectiveMatrixManager.pMatrix);
393
528
  return cartesianToSpherical(pickerPoint);
394
529
  }
530
+ collectViewportSphericalSamples(sampleCount = 5) {
531
+ const rect = this.canvas.getBoundingClientRect();
532
+ const width = rect.width;
533
+ const height = rect.height;
534
+ const samples = [];
535
+ for (let ix = 0; ix < sampleCount; ix++) {
536
+ const x = sampleCount === 1 ? width / 2 : (ix / (sampleCount - 1)) * width;
537
+ for (let iy = 0; iy < sampleCount; iy++) {
538
+ const y = sampleCount === 1 ? height / 2 : (iy / (sampleCount - 1)) * height;
539
+ const hit = RayPickingUtils.getIntersectionPointWithSingleModel(x, y, this._healpixGrid, this._webgl, this._camera, this._perspectiveMatrixManager.pMatrix);
540
+ if (hit && hit.length > 0) {
541
+ samples.push(cartesianToSpherical(hit));
542
+ }
543
+ }
544
+ }
545
+ return samples;
546
+ }
395
547
  activateHiPS(hipsDescriptor) {
396
548
  this._activeHiPS = new HiPS(1, [0.0, 0.0, 0.0], 0, 0, hipsDescriptor, this._webgl, this._healpixGrid);
549
+ this._activeBaseLayer = 'hips';
550
+ }
551
+ activateXYZ(config) {
552
+ this.activateXYZ2(new XYZMapDescriptor(config.name ?? 'XYZ Earth2 Layer', config.urlTemplate, config.minZoom ?? 0, config.maxZoom ?? 8, config.segmentsPerSide ?? 48, config.maxCachedTiles ?? 384, 8, config.urlResolver));
553
+ this._activeBaseLayer = 'xyz';
554
+ }
555
+ activateXYZ2(config) {
556
+ this._activeXYZ2 = new XYZMap(1, [0.0, 0.0, 0.0], 0, 0, config, this._webgl);
557
+ this._activeBaseLayer = 'xyz';
558
+ }
559
+ activateWMTS(config) {
560
+ const adapter = new WMTSAdapter(config);
561
+ const xyzConfig = adapter.toXYZLayerConfig();
562
+ this._activeXYZ2 = new XYZMap(1, [0.0, 0.0, 0.0], 0, 0, new XYZMapDescriptor(config.layer ? `WMTS ${config.layer}` : 'WMTS Earth2 Layer', xyzConfig.urlTemplate, xyzConfig.minZoom ?? 0, xyzConfig.maxZoom ?? 8, xyzConfig.segmentsPerSide ?? 48, xyzConfig.maxCachedTiles ?? 384, 8, xyzConfig.urlResolver), this._webgl);
563
+ this._activeBaseLayer = 'xyz';
397
564
  }
398
565
  // Catalogue section
399
566
  async showCatalogue(cat) {
@@ -427,7 +594,39 @@ class AstroSphere {
427
594
  goTo(raDeg, decDeg) {
428
595
  this._camera.goTo(raDeg, decDeg);
429
596
  }
597
+ getActiveCoordinateMode() {
598
+ if (this._activeBaseLayer === 'xyz') {
599
+ return 'lonlat';
600
+ }
601
+ if (this._activeBaseLayer === 'hips' && this._activeHiPS?.isGalacticHips) {
602
+ return 'galactic';
603
+ }
604
+ return 'equatorial';
605
+ }
606
+ resetAxesOrientation() {
607
+ const center = this.updateCentralPoint();
608
+ if (!center)
609
+ return;
610
+ this.inertiaX = 0;
611
+ this.inertiaY = 0;
612
+ this._camera.goTo(center.astroDeg.ra, center.astroDeg.dec);
613
+ this._perspectiveMatrixManager.computePerspectiveMatrix(this.canvas, this._camera, bootSetup.camera_fov_deg, bootSetup.camera_near_plane, global.insideSphere);
614
+ this.updateCentralPoint();
615
+ this._cameraStatusChanged = true;
616
+ }
617
+ setKeepCameraNorthUp(enabled) {
618
+ this.keepCameraNorthUp = enabled;
619
+ if (enabled) {
620
+ this.resetAxesOrientation();
621
+ }
622
+ }
623
+ isKeepCameraNorthUp() {
624
+ return this.keepCameraNorthUp;
625
+ }
430
626
  getFoV() {
627
+ if (this._activeBaseLayer === 'xyz' && this._activeXYZ2) {
628
+ return this._activeXYZ2.getFoV();
629
+ }
431
630
  return this.fov;
432
631
  }
433
632
  getFoVPolygon() {
@@ -513,10 +712,11 @@ class AstroSphere {
513
712
  // return null
514
713
  }
515
714
  changeColorMap(cm) {
516
- if (!this._activeHiPS)
715
+ if (!this._activeHiPS && !this._activeXYZ2)
517
716
  return;
518
717
  this._selectedColorMap = cm;
519
718
  this._activeHiPS?.changeColorMap(cm);
719
+ this._activeXYZ2?.changeColorMap(cm);
520
720
  }
521
721
  prevFov = 0;
522
722
  prevCentralRaDeg = null;
@@ -524,12 +724,46 @@ class AstroSphere {
524
724
  get activeHiPS() {
525
725
  return this._activeHiPS;
526
726
  }
727
+ get activeXYZ() {
728
+ return this._activeXYZ2;
729
+ }
730
+ isLonLatGridVisible() {
731
+ return this._activeXYZ2?.isLonLatGridVisible() ?? false;
732
+ }
733
+ toggleLonLatGrid() {
734
+ return this._activeXYZ2?.toggleLonLatGrid() ?? false;
735
+ }
736
+ setEastWestRotationLocked(locked) {
737
+ this._camera.setRotationLock({ y: locked });
738
+ if (locked)
739
+ this.inertiaX = 0;
740
+ this.lockedEastWestRaDeg = locked ? this.updateCentralPoint()?.astroDeg.ra ?? null : null;
741
+ }
742
+ isEastWestRotationLocked() {
743
+ return this._camera.isRotationLockedY();
744
+ }
745
+ setNorthSouthRotationLocked(locked) {
746
+ this._camera.setRotationLock({ x: locked });
747
+ if (locked)
748
+ this.inertiaY = 0;
749
+ this.lockedNorthSouthDecDeg = locked ? this.updateCentralPoint()?.astroDeg.dec ?? null : null;
750
+ }
751
+ isNorthSouthRotationLocked() {
752
+ return this._camera.isRotationLockedX();
753
+ }
754
+ getXYZDebugStats() {
755
+ return {
756
+ activeBaseLayer: this._activeBaseLayer,
757
+ layer: this._activeXYZ2?.getDebugStats() ?? null,
758
+ requests: xyzTileRequestScheduler.getDebugStats(),
759
+ };
760
+ }
527
761
  draw(canvas) {
528
762
  if (this._refreshingStatus)
529
763
  return;
530
764
  if (!this._webgl)
531
765
  return;
532
- if (!this._activeHiPS)
766
+ if (!this._activeHiPS && !this._activeXYZ2)
533
767
  return;
534
768
  if (!this._healpixGrid || Object.keys(this._healpixGrid).length === 0)
535
769
  return;
@@ -566,12 +800,17 @@ class AstroSphere {
566
800
  // Rotation inertia
567
801
  if (this.mouseDown || Math.abs(this.inertiaX) > 0.02 || Math.abs(this.inertiaY) > 0.02) {
568
802
  cameraRotated = true;
569
- THETA = this.inertiaY;
570
- PHI = this.inertiaX;
571
- this.inertiaX *= 0.95;
572
- this.inertiaY *= 0.95;
803
+ const filteredInertia = this.filterRotationDeltaByAstroLocks(this.inertiaX, this.inertiaY);
804
+ PHI = filteredInertia.deltaX;
805
+ THETA = filteredInertia.deltaY;
806
+ this.inertiaX = filteredInertia.deltaX * 0.95;
807
+ this.inertiaY = filteredInertia.deltaY * 0.95;
573
808
  this._camera.rotate(PHI, THETA);
574
809
  this._perspectiveMatrixManager.computePerspectiveMatrix(canvas, this._camera, bootSetup.camera_fov_deg, bootSetup.camera_near_plane, global.insideSphere);
810
+ const lockCorrected = this.enforceAstronomicalRotationLocks();
811
+ if (!lockCorrected) {
812
+ this.enforceCameraNorthUp();
813
+ }
575
814
  }
576
815
  else {
577
816
  this.inertiaY = 0;
@@ -613,15 +852,25 @@ class AstroSphere {
613
852
  this._webgl.enable(this._webgl.CULL_FACE);
614
853
  this._webgl.cullFace(global.insideSphere ? this._webgl.FRONT : this._webgl.BACK);
615
854
  this._webgl.blendFunc(this._webgl.SRC_ALPHA, this._webgl.ONE_MINUS_SRC_ALPHA);
616
- const visibleOrder = Math.min(this._healpixGrid.visibleorder, this._activeHiPS.maxOrder);
617
- this._healpixGrid.visibleTilesManager.computeVisiblePixels(visibleOrder, this._webgl, this._camera, this._perspectiveMatrixManager.pMatrix);
855
+ if (this._activeBaseLayer === 'hips' && this._activeHiPS) {
856
+ const visibleOrder = Math.min(this._healpixGrid.visibleorder, this._activeHiPS.maxOrder);
857
+ this._healpixGrid.visibleTilesManager.computeVisiblePixels(visibleOrder, this._webgl, this._camera, this._perspectiveMatrixManager.pMatrix);
858
+ }
618
859
  // DRAW HiPS
619
860
  const skyEntityDrawInput = {
620
861
  fovDeg: this._healpixGrid.getMinFoV(),
621
862
  camera: this._camera,
622
- pMatrix: this._perspectiveMatrixManager.pMatrix
863
+ pMatrix: this._perspectiveMatrixManager.pMatrix,
864
+ centerSphericalDeg: this.updateCentralPoint().sphericalDeg,
865
+ fovPolygon: this._activeBaseLayer === 'xyz' ? this.getFoVPolygon() : undefined,
866
+ viewportSphericalSamples: this._activeBaseLayer === 'xyz' ? this.collectViewportSphericalSamples(7) : undefined,
623
867
  };
624
- this._activeHiPS.draw(skyEntityDrawInput);
868
+ if (this._activeBaseLayer === 'hips') {
869
+ this._activeHiPS?.draw(skyEntityDrawInput);
870
+ }
871
+ if (this._activeBaseLayer === 'xyz') {
872
+ this._activeXYZ2?.draw(skyEntityDrawInput);
873
+ }
625
874
  this._healpixGrid.draw(skyEntityDrawInput);
626
875
  this._equatorialGrid.draw(skyEntityDrawInput);
627
876
  this._webgl.enable(this._webgl.DEPTH_TEST);
@@ -642,14 +891,16 @@ class AstroSphere {
642
891
  });
643
892
  }
644
893
  this.activeCatalogues.forEach(cat => {
645
- if (this._activeHiPS) {
646
- cat.draw(this._activeHiPS.getModelMatrix(), this.mouseHelper, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
894
+ const activeModelMatrix = this._activeHiPS?.getModelMatrix() ?? this._activeXYZ2?.getModelMatrix();
895
+ if (activeModelMatrix) {
896
+ cat.draw(activeModelMatrix, this.mouseHelper, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
647
897
  }
648
898
  });
649
899
  this.emitHoveredSourceIfChanged();
650
900
  this.activeFootprintSets.forEach(fst => {
651
- if (this._activeHiPS) {
652
- fst.draw(this._activeHiPS.getModelMatrix(), this.mouseHelper, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
901
+ const activeModelMatrix = this._activeHiPS?.getModelMatrix() ?? this._activeXYZ2?.getModelMatrix();
902
+ if (activeModelMatrix) {
903
+ fst.draw(activeModelMatrix, this.mouseHelper, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
653
904
  }
654
905
  });
655
906
  }
@@ -1,5 +1,5 @@
1
1
  import { HiPSDescriptor } from './model/hips/HiPSDescriptor.js';
2
- import { FoV } from './model/FoV.js';
2
+ import { SphereFoV } from './model/SphereFoV.js';
3
3
  import { Point } from './model/Point.js';
4
4
  import { CatalogueGL } from './model/catalogues/CatalogueGL.js';
5
5
  import type { CameraChangedDetail, PointCoordinates } from './AstroSphere.js';
@@ -9,6 +9,10 @@ import Camera from './Camera.js';
9
9
  import { ReadonlyMat4 } from 'gl-matrix';
10
10
  import { ColorMap, ColorMapName } from './model/ColorMaps.js';
11
11
  import { HiPS } from './model/hips/HiPS.js';
12
+ import type { WMTSLayerConfig, XYZDebugStats, XYZLayerConfig } from './model/earth/XYZConfig.js';
13
+ import { XYZMap } from './model/earth/XYZMap.js';
14
+ import { TerraPointSetGL } from './model/terra/TerraPointSetGL.js';
15
+ import { TerraFootprintSetGL } from './model/terra/TerraFootprintSetGL.js';
12
16
  export declare class AstroViewer {
13
17
  private astroSphere;
14
18
  private canvas;
@@ -32,20 +36,38 @@ export declare class AstroViewer {
32
36
  showFootprintSet(footprintSet: FootprintSetGL): void;
33
37
  hideFootprintSet(footprintSet: FootprintSetGL, isVisible: boolean): void;
34
38
  deleteFootprintSet(footprintSet: FootprintSetGL): void;
39
+ createTerraPointSet(pointSetName: string, pointSetDescription: string, providerUrl: string, metadataManager: MetadataManager): TerraPointSetGL;
40
+ showTerraPointSet(pointSet: TerraPointSetGL): void;
41
+ hideTerraPointSet(pointSet: TerraPointSetGL, isVisible: boolean): void;
42
+ deleteTerraPointSet(pointSet: TerraPointSetGL): void;
43
+ createTerraFootprintSet(footprintSetName: string, footprintSetDescription: string, providerUrl: string, metadataManager: MetadataManager): TerraFootprintSetGL;
44
+ showTerraFootprintSet(footprintSet: TerraFootprintSetGL): void;
45
+ hideTerraFootprintSet(footprintSet: TerraFootprintSetGL, isVisible: boolean): void;
46
+ deleteTerraFootprintSet(footprintSet: TerraFootprintSetGL): void;
35
47
  changeFootprintSetColor(footprintSet: FootprintSetGL, hexColor: string): void;
36
48
  getHoveredFootprints(): HoveredFootprintDetail[];
37
49
  getDefaultHiPSURL(): string;
38
50
  activateHiPS(hipsDescriptor: HiPSDescriptor): void;
51
+ activateXYZ(config: XYZLayerConfig): void;
52
+ activateXYZ2(config: XYZLayerConfig & {
53
+ name?: string;
54
+ }): void;
55
+ activateWMTS(config: WMTSLayerConfig): void;
56
+ setXYZMaxConcurrentRequests(value: number): void;
57
+ getXYZMaxConcurrentRequests(): number;
58
+ getXYZDebugStats(): XYZDebugStats;
39
59
  loadHiPS(baseUrl: string): Promise<string>;
40
60
  changeColorMap(colorMapName: ColorMapName): void;
41
61
  changeCustomColorMap(colorMap: ColorMap): void;
42
62
  getActiveHiPS(): HiPS | null;
63
+ getActiveXYZ(): XYZMap | null;
43
64
  setCamera(camera: Camera): void;
44
65
  setCameraPosition(pos: [number, number, number]): void;
45
66
  setCameraMatrix(viewMatrix: Float32Array): void;
46
67
  restoreAstroViewerState(detail: CameraChangedDetail, applyColorMap: boolean): void;
47
68
  getCurrentAstroViewerStatus(): CameraChangedDetail | null;
48
69
  goTo(raDeg: number, decDeg: number): void;
70
+ getActiveCoordinateMode(): 'equatorial' | 'galactic' | 'lonlat';
49
71
  getCenterCoordinates(): PointCoordinates | undefined;
50
72
  getCoordinatesFromMouse(): PointCoordinates | undefined;
51
73
  setModelMatrix(modelMatrix: ReadonlyMat4): void;
@@ -53,7 +75,16 @@ export declare class AstroViewer {
53
75
  isHealpixGridVisible(): boolean;
54
76
  toggleEquatorialGrid(): void;
55
77
  isEquatorialGridVisible(): boolean;
56
- getFoV(): FoV;
78
+ toggleLonLatGrid(): boolean;
79
+ isLonLatGridVisible(): boolean;
80
+ setEastWestRotationLocked(locked: boolean): void;
81
+ isEastWestRotationLocked(): boolean;
82
+ setNorthSouthRotationLocked(locked: boolean): void;
83
+ isNorthSouthRotationLocked(): boolean;
84
+ resetAxesOrientation(): void;
85
+ setKeepCameraNorthUp(enabled: boolean): void;
86
+ isKeepCameraNorthUp(): boolean;
87
+ getFoV(): SphereFoV;
57
88
  getFoVPolygon(): Point[];
58
89
  changeFoV(deg: number): void;
59
90
  changeFoV2(deg: number): void;
@@ -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 global from './Global.js'
2
14
  import AstroSphere from './AstroSphere.js';
3
15
  import { HiPSDescriptor } from './model/hips/HiPSDescriptor.js';
@@ -5,6 +17,10 @@ import { CatalogueGL } from './model/catalogues/CatalogueGL.js';
5
17
  import { FootprintSetGL } from './model/footprints/FootprintSetGL.js';
6
18
  import { bootSetup } from './Config.js';
7
19
  import ColorMaps from './model/ColorMaps.js';
20
+ import { xyzTileRequestScheduler } from './model/earth/XYZTileRequestScheduler.js';
21
+ import { XYZMapDescriptor } from './model/earth/XYZMapDescriptor.js';
22
+ import { TerraPointSetGL } from './model/terra/TerraPointSetGL.js';
23
+ import { TerraFootprintSetGL } from './model/terra/TerraFootprintSetGL.js';
8
24
  // & {
9
25
  // viewportWidth: number
10
26
  // viewportHeight: number
@@ -71,6 +87,31 @@ export class AstroViewer {
71
87
  deleteFootprintSet(footprintSet) {
72
88
  this.astroSphere.deleteFootprintSet(footprintSet);
73
89
  }
90
+ // TERRA OVERLAYS
91
+ createTerraPointSet(pointSetName, pointSetDescription, providerUrl, metadataManager) {
92
+ return new TerraPointSetGL(pointSetName, pointSetDescription, providerUrl, metadataManager, this.webgl, this.astroSphere.healpixGrid.visibleTilesManager);
93
+ }
94
+ showTerraPointSet(pointSet) {
95
+ this.astroSphere.showCatalogue(pointSet);
96
+ }
97
+ hideTerraPointSet(pointSet, isVisible) {
98
+ pointSet.setIsVisible(isVisible);
99
+ }
100
+ deleteTerraPointSet(pointSet) {
101
+ this.astroSphere.deleteCatalogue(pointSet);
102
+ }
103
+ createTerraFootprintSet(footprintSetName, footprintSetDescription, providerUrl, metadataManager) {
104
+ return new TerraFootprintSetGL(footprintSetName, footprintSetDescription, providerUrl, metadataManager, this.webgl, this.astroSphere.healpixGrid.visibleTilesManager);
105
+ }
106
+ showTerraFootprintSet(footprintSet) {
107
+ this.astroSphere.showFootprintSet(footprintSet);
108
+ }
109
+ hideTerraFootprintSet(footprintSet, isVisible) {
110
+ footprintSet.setIsVisible(isVisible);
111
+ }
112
+ deleteTerraFootprintSet(footprintSet) {
113
+ this.astroSphere.deleteFootprintSet(footprintSet);
114
+ }
74
115
  changeFootprintSetColor(footprintSet, hexColor) {
75
116
  // footprintSet.footprintsetProps.changeColor(hexColor)
76
117
  footprintSet.changeColor(hexColor);
@@ -85,6 +126,25 @@ export class AstroViewer {
85
126
  activateHiPS(hipsDescriptor) {
86
127
  this.astroSphere.activateHiPS(hipsDescriptor);
87
128
  }
129
+ activateXYZ(config) {
130
+ this.astroSphere.activateXYZ(config);
131
+ }
132
+ activateXYZ2(config) {
133
+ const descriptor = new XYZMapDescriptor(config.name ?? 'XYZ Earth2 Layer', config.urlTemplate, config.minZoom ?? 0, config.maxZoom ?? 8, config.segmentsPerSide ?? 48, config.maxCachedTiles ?? 384, 8, config.urlResolver);
134
+ this.astroSphere.activateXYZ2(descriptor);
135
+ }
136
+ activateWMTS(config) {
137
+ this.astroSphere.activateWMTS(config);
138
+ }
139
+ setXYZMaxConcurrentRequests(value) {
140
+ xyzTileRequestScheduler.setMaxConcurrent(value);
141
+ }
142
+ getXYZMaxConcurrentRequests() {
143
+ return xyzTileRequestScheduler.getMaxConcurrent();
144
+ }
145
+ getXYZDebugStats() {
146
+ return this.astroSphere.getXYZDebugStats();
147
+ }
88
148
  async loadHiPS(baseUrl) {
89
149
  const hipsUrl = baseUrl.endsWith('/') ? baseUrl : baseUrl + '/';
90
150
  const resp = await fetch(hipsUrl + 'properties');
@@ -108,6 +168,9 @@ export class AstroViewer {
108
168
  getActiveHiPS() {
109
169
  return this.astroSphere.activeHiPS;
110
170
  }
171
+ getActiveXYZ() {
172
+ return this.astroSphere.activeXYZ;
173
+ }
111
174
  // Camera: GOTOs and COORDS
112
175
  setCamera(camera) {
113
176
  this.astroSphere.setCamera(camera);
@@ -128,6 +191,9 @@ export class AstroViewer {
128
191
  // console.log(`AstroViewer.goTo goto(${raDeg}, ${decDeg})`)
129
192
  this.astroSphere.goTo(raDeg, decDeg);
130
193
  }
194
+ getActiveCoordinateMode() {
195
+ return this.astroSphere.getActiveCoordinateMode();
196
+ }
131
197
  getCenterCoordinates() {
132
198
  return this.astroSphere.getCentralPointCoordinates();
133
199
  }
@@ -154,6 +220,33 @@ export class AstroViewer {
154
220
  // return equatorialGridSingleton.isVisible()
155
221
  return this.astroSphere.equatorialGrid.isVisible();
156
222
  }
223
+ toggleLonLatGrid() {
224
+ return this.astroSphere.toggleLonLatGrid();
225
+ }
226
+ isLonLatGridVisible() {
227
+ return this.astroSphere.isLonLatGridVisible();
228
+ }
229
+ setEastWestRotationLocked(locked) {
230
+ this.astroSphere.setEastWestRotationLocked(locked);
231
+ }
232
+ isEastWestRotationLocked() {
233
+ return this.astroSphere.isEastWestRotationLocked();
234
+ }
235
+ setNorthSouthRotationLocked(locked) {
236
+ this.astroSphere.setNorthSouthRotationLocked(locked);
237
+ }
238
+ isNorthSouthRotationLocked() {
239
+ return this.astroSphere.isNorthSouthRotationLocked();
240
+ }
241
+ resetAxesOrientation() {
242
+ this.astroSphere.resetAxesOrientation();
243
+ }
244
+ setKeepCameraNorthUp(enabled) {
245
+ this.astroSphere.setKeepCameraNorthUp(enabled);
246
+ }
247
+ isKeepCameraNorthUp() {
248
+ return this.astroSphere.isKeepCameraNorthUp();
249
+ }
157
250
  // FOV
158
251
  getFoV() {
159
252
  return this.astroSphere.getFoV();