astro-viewer 3.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 (110) hide show
  1. package/dist/astroviewer.cjs +2505 -1335
  2. package/dist/astroviewer.js +1 -1
  3. package/dist/astroviewer.min.js +1 -1
  4. package/lib-esm/AstroSphere.d.ts +26 -6
  5. package/lib-esm/AstroSphere.js +215 -18
  6. package/lib-esm/AstroViewer.d.ts +18 -5
  7. package/lib-esm/AstroViewer.js +47 -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 +6 -4
  12. package/lib-esm/index.js +16 -3
  13. package/lib-esm/model/AbstractSkyEntity.js +12 -0
  14. package/lib-esm/model/AstroGL.js +12 -0
  15. package/lib-esm/model/ColorMaps.js +12 -0
  16. package/lib-esm/model/MetadataColumn.js +12 -0
  17. package/lib-esm/model/MetadataManager.js +12 -0
  18. package/lib-esm/model/Point.js +12 -0
  19. package/lib-esm/model/Point2D.js +12 -0
  20. package/lib-esm/model/Source.js +12 -0
  21. package/lib-esm/model/SphereFoV.d.ts +30 -0
  22. package/lib-esm/model/SphereFoV.js +195 -0
  23. package/lib-esm/model/catalogues/CatalogueGL.js +12 -0
  24. package/lib-esm/model/catalogues/CatalogueProps.js +13 -1
  25. package/lib-esm/model/earth/{wmts/WMTSAdapter.d.ts → WMTSAdapter.d.ts} +2 -1
  26. package/lib-esm/model/earth/{wmts/WMTSAdapter.js → WMTSAdapter.js} +12 -0
  27. package/lib-esm/model/earth/XYZAnchestorTile.d.ts +17 -0
  28. package/lib-esm/model/earth/XYZAnchestorTile.js +87 -0
  29. package/lib-esm/model/earth/{types.d.ts → XYZConfig.d.ts} +2 -17
  30. package/lib-esm/model/earth/XYZConfig.js +13 -0
  31. package/lib-esm/model/earth/XYZFoVHelper.d.ts +9 -0
  32. package/lib-esm/model/earth/XYZFoVHelper.js +101 -0
  33. package/lib-esm/model/earth/XYZMap.d.ts +31 -0
  34. package/lib-esm/model/earth/XYZMap.js +208 -0
  35. package/lib-esm/model/earth/XYZMapDescriptor.d.ts +29 -0
  36. package/lib-esm/model/earth/XYZMapDescriptor.js +77 -0
  37. package/lib-esm/model/earth/XYZMeshBuilder.d.ts +1 -1
  38. package/lib-esm/model/earth/XYZMeshBuilder.js +13 -2
  39. package/lib-esm/model/earth/XYZTile.d.ts +20 -21
  40. package/lib-esm/model/earth/XYZTile.js +82 -102
  41. package/lib-esm/model/earth/XYZTileBuffer.d.ts +24 -26
  42. package/lib-esm/model/earth/XYZTileBuffer.js +60 -70
  43. package/lib-esm/model/earth/XYZTileRequestScheduler.d.ts +1 -1
  44. package/lib-esm/model/earth/XYZTileRequestScheduler.js +13 -2
  45. package/lib-esm/model/earth/XYZTypes.d.ts +24 -0
  46. package/lib-esm/model/earth/XYZTypes.js +13 -0
  47. package/lib-esm/model/earth/XYZVisibleTilesManager.d.ts +16 -27
  48. package/lib-esm/model/earth/XYZVisibleTilesManager.js +61 -192
  49. package/lib-esm/model/footprints/Footprint.js +12 -0
  50. package/lib-esm/model/footprints/FootprintProps.js +13 -1
  51. package/lib-esm/model/footprints/FootprintSetGL.js +12 -0
  52. package/lib-esm/model/grid/EquatorialGrid.js +20 -6
  53. package/lib-esm/model/grid/GridTextHelper.d.ts +10 -19
  54. package/lib-esm/model/grid/GridTextHelper.js +70 -67
  55. package/lib-esm/model/grid/HealpixGrid.d.ts +4 -4
  56. package/lib-esm/model/grid/HealpixGrid.js +19 -6
  57. package/lib-esm/model/grid/LonLatGrid.d.ts +40 -0
  58. package/lib-esm/model/grid/LonLatGrid.js +258 -0
  59. package/lib-esm/model/hips/AllSky.js +12 -0
  60. package/lib-esm/model/hips/AncestorTile.js +12 -0
  61. package/lib-esm/model/hips/FoVHelper.js +12 -0
  62. package/lib-esm/model/hips/HiPS.js +12 -9
  63. package/lib-esm/model/hips/HiPSDescriptor.js +12 -0
  64. package/lib-esm/model/hips/Tile.js +12 -0
  65. package/lib-esm/model/hips/TileBuffer.js +12 -0
  66. package/lib-esm/model/hips/VisibleTilesManager.js +12 -0
  67. package/lib-esm/model/tap/TapMetadata.js +12 -0
  68. package/lib-esm/model/tap/TapMetadataList.js +12 -0
  69. package/lib-esm/model/tap/TapRepo.js +12 -0
  70. package/lib-esm/model/terra/TerraFootprintSetGL.js +12 -0
  71. package/lib-esm/model/terra/TerraPointSetGL.js +12 -0
  72. package/lib-esm/services/SesameService.js +11 -3
  73. package/lib-esm/services/helpers.js +12 -0
  74. package/lib-esm/services/hipsNodeService.js +12 -0
  75. package/lib-esm/services/parse-hipslist.js +12 -0
  76. package/lib-esm/services/queryCatalogueByFoV.js +13 -1
  77. package/lib-esm/services/queryFootprintSetByFov.js +13 -1
  78. package/lib-esm/services/tapRepoService.js +13 -1
  79. package/lib-esm/services/types.js +12 -0
  80. package/lib-esm/shader/CatalogueShaderProgram.js +12 -0
  81. package/lib-esm/shader/FootprintShaderProgram.js +12 -0
  82. package/lib-esm/shader/GridShaderManager.js +12 -0
  83. package/lib-esm/shader/HiPSShaderProgram.js +12 -0
  84. package/lib-esm/shader/ShaderManager.js +12 -1
  85. package/lib-esm/shader/ShaderManagerMutliHiPS.js +12 -1
  86. package/lib-esm/shader/XYZShaderProgram.d.ts +14 -1
  87. package/lib-esm/shader/XYZShaderProgram.js +127 -2
  88. package/lib-esm/utils/CoordsType.js +12 -0
  89. package/lib-esm/utils/FoVUtils.js +12 -0
  90. package/lib-esm/utils/GeomUtils.js +12 -0
  91. package/lib-esm/utils/MouseHelper.js +12 -0
  92. package/lib-esm/utils/PerspectiveMatrixManager.js +12 -0
  93. package/lib-esm/utils/RayPickingUtils.js +12 -0
  94. package/lib-esm/utils/STCSParser.js +12 -0
  95. package/lib-esm/utils/ShaderUtility.js +13 -1
  96. package/lib-esm/utils/Utils.js +12 -0
  97. package/lib-esm/utils/XYZRayPickingUtils.d.ts +27 -0
  98. package/lib-esm/utils/XYZRayPickingUtils.js +226 -0
  99. package/package.json +1 -1
  100. package/lib-esm/model/FoV.d.ts +0 -62
  101. package/lib-esm/model/FoV.js +0 -274
  102. package/lib-esm/model/earth/XYZAncestorMeshCache.d.ts +0 -10
  103. package/lib-esm/model/earth/XYZAncestorMeshCache.js +0 -31
  104. package/lib-esm/model/earth/XYZLayer.d.ts +0 -31
  105. package/lib-esm/model/earth/XYZLayer.js +0 -231
  106. package/lib-esm/model/earth/XYZTileProvider.d.ts +0 -29
  107. package/lib-esm/model/earth/XYZTileProvider.js +0 -187
  108. package/lib-esm/model/earth/types.js +0 -1
  109. package/lib-esm/model/earth/wmts/types.d.ts +0 -1
  110. package/lib-esm/model/earth/wmts/types.js +0 -1
@@ -2,15 +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 { XYZLayer } from './model/earth/XYZLayer.js';
13
- import type { WMTSLayerConfig, XYZDebugStats, XYZLayerConfig } from './model/earth/types.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';
14
15
  export type PointCoordinates = {
15
16
  astroDeg: AstroCoords;
16
17
  raHMS: HMS;
@@ -56,7 +57,7 @@ declare class AstroSphere {
56
57
  private pointerDownY;
57
58
  private pointerDownAt;
58
59
  private _activeHiPS;
59
- private _activeXYZ;
60
+ private _activeXYZ2;
60
61
  private _activeBaseLayer;
61
62
  private startup;
62
63
  private fov;
@@ -68,6 +69,9 @@ declare class AstroSphere {
68
69
  private lastHoveredSource;
69
70
  private lastHoveredCatalogue;
70
71
  private zoomSensitivity;
72
+ private lockedEastWestRaDeg;
73
+ private lockedNorthSouthDecDeg;
74
+ private keepCameraNorthUp;
71
75
  constructor(canvas: HTMLCanvasElement, webgl: WebGL2RenderingContext);
72
76
  private initCamera;
73
77
  setCamera(camera: Camera): void;
@@ -84,12 +88,18 @@ declare class AstroSphere {
84
88
  private computeZoomStep;
85
89
  setZoomSensitivity(value: number): void;
86
90
  getZoomSensitivity(): number;
91
+ private filterRotationDeltaByAstroLocks;
92
+ private projectModelDirectionToScreen;
93
+ private projectModelPointToScreen;
94
+ private enforceAstronomicalRotationLocks;
95
+ private enforceCameraNorthUp;
87
96
  private emitCameraChanged;
88
97
  private addEventListeners;
89
98
  getPhiThetaDeg(canvas: HTMLCanvasElement): SphericalCoords;
90
99
  private collectViewportSphericalSamples;
91
100
  activateHiPS(hipsDescriptor: HiPSDescriptor): void;
92
101
  activateXYZ(config: XYZLayerConfig): void;
102
+ activateXYZ2(config: XYZMapDescriptor): void;
93
103
  activateWMTS(config: WMTSLayerConfig): void;
94
104
  showCatalogue(cat: CatalogueGL): Promise<CatalogueGL>;
95
105
  deleteCatalogue(catalogue: CatalogueGL): void;
@@ -97,7 +107,11 @@ declare class AstroSphere {
97
107
  deleteFootprintSet(footprintSet: FootprintSetGL): void;
98
108
  getHoveredFootprints(): HoveredFootprintDetail[];
99
109
  goTo(raDeg: number, decDeg: number): void;
100
- getFoV(): FoV;
110
+ getActiveCoordinateMode(): 'equatorial' | 'galactic' | 'lonlat';
111
+ resetAxesOrientation(): void;
112
+ setKeepCameraNorthUp(enabled: boolean): void;
113
+ isKeepCameraNorthUp(): boolean;
114
+ getFoV(): SphereFoV;
101
115
  getFoVPolygon(): Point[];
102
116
  changeFoV(deg: number): void;
103
117
  changeFoV2(deg: number): void;
@@ -114,7 +128,13 @@ declare class AstroSphere {
114
128
  private prevCentralRaDeg;
115
129
  private prevCentralDecDeg;
116
130
  get activeHiPS(): HiPS | null;
117
- get activeXYZ(): XYZLayer | 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;
118
138
  getXYZDebugStats(): XYZDebugStats;
119
139
  draw(canvas: HTMLCanvasElement): void;
120
140
  private emitHoveredSourceIfChanged;
@@ -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,9 +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';
15
- import { XYZLayer } from './model/earth/XYZLayer.js';
16
27
  import { xyzTileRequestScheduler } from './model/earth/XYZTileRequestScheduler.js';
17
- import { WMTSAdapter } from './model/earth/wmts/WMTSAdapter.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';
18
32
  /**
19
33
  * AstroSphere — main WebGL scene controller (TS port)
20
34
  */
@@ -39,7 +53,7 @@ class AstroSphere {
39
53
  pointerDownY = null;
40
54
  pointerDownAt = 0;
41
55
  _activeHiPS = null;
42
- _activeXYZ = null;
56
+ _activeXYZ2 = null;
43
57
  _activeBaseLayer = null;
44
58
  startup = true;
45
59
  fov;
@@ -51,6 +65,9 @@ class AstroSphere {
51
65
  lastHoveredSource = null;
52
66
  lastHoveredCatalogue = null;
53
67
  zoomSensitivity = 1.0;
68
+ lockedEastWestRaDeg = null;
69
+ lockedNorthSouthDecDeg = null;
70
+ keepCameraNorthUp = false;
54
71
  constructor(canvas, webgl) {
55
72
  console.log('[AstroSphere] new instance for canvas', canvas.id);
56
73
  // Keep global GL context (as in original JS)
@@ -150,6 +167,118 @@ class AstroSphere {
150
167
  getZoomSensitivity() {
151
168
  return this.zoomSensitivity;
152
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
+ }
153
282
  emitCameraChanged(reason) {
154
283
  // avoid dispatch before scene is ready
155
284
  if (!this._activeHiPS)
@@ -266,8 +395,9 @@ class AstroSphere {
266
395
  // Rotation deltas – either use client-space or local-space, but be consistent
267
396
  const deltaX = ((newX - (this.lastMouseX ?? newX)) * Math.PI) / canvas.width;
268
397
  const deltaY = ((newY - (this.lastMouseY ?? newY)) * Math.PI) / canvas.height;
269
- this.inertiaX += 0.1 * deltaX;
270
- 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;
271
401
  this.updateCentralPoint();
272
402
  }
273
403
  else {
@@ -419,12 +549,17 @@ class AstroSphere {
419
549
  this._activeBaseLayer = 'hips';
420
550
  }
421
551
  activateXYZ(config) {
422
- this._activeXYZ = new XYZLayer(config, this._webgl);
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);
423
557
  this._activeBaseLayer = 'xyz';
424
558
  }
425
559
  activateWMTS(config) {
426
560
  const adapter = new WMTSAdapter(config);
427
- this._activeXYZ = new XYZLayer(adapter.toXYZLayerConfig(), this._webgl);
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);
428
563
  this._activeBaseLayer = 'xyz';
429
564
  }
430
565
  // Catalogue section
@@ -459,7 +594,39 @@ class AstroSphere {
459
594
  goTo(raDeg, decDeg) {
460
595
  this._camera.goTo(raDeg, decDeg);
461
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
+ }
462
626
  getFoV() {
627
+ if (this._activeBaseLayer === 'xyz' && this._activeXYZ2) {
628
+ return this._activeXYZ2.getFoV();
629
+ }
463
630
  return this.fov;
464
631
  }
465
632
  getFoVPolygon() {
@@ -545,10 +712,11 @@ class AstroSphere {
545
712
  // return null
546
713
  }
547
714
  changeColorMap(cm) {
548
- if (!this._activeHiPS)
715
+ if (!this._activeHiPS && !this._activeXYZ2)
549
716
  return;
550
717
  this._selectedColorMap = cm;
551
718
  this._activeHiPS?.changeColorMap(cm);
719
+ this._activeXYZ2?.changeColorMap(cm);
552
720
  }
553
721
  prevFov = 0;
554
722
  prevCentralRaDeg = null;
@@ -557,12 +725,36 @@ class AstroSphere {
557
725
  return this._activeHiPS;
558
726
  }
559
727
  get activeXYZ() {
560
- return this._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();
561
753
  }
562
754
  getXYZDebugStats() {
563
755
  return {
564
756
  activeBaseLayer: this._activeBaseLayer,
565
- layer: this._activeXYZ?.getDebugStats() ?? null,
757
+ layer: this._activeXYZ2?.getDebugStats() ?? null,
566
758
  requests: xyzTileRequestScheduler.getDebugStats(),
567
759
  };
568
760
  }
@@ -571,7 +763,7 @@ class AstroSphere {
571
763
  return;
572
764
  if (!this._webgl)
573
765
  return;
574
- if (!this._activeHiPS && !this._activeXYZ)
766
+ if (!this._activeHiPS && !this._activeXYZ2)
575
767
  return;
576
768
  if (!this._healpixGrid || Object.keys(this._healpixGrid).length === 0)
577
769
  return;
@@ -608,12 +800,17 @@ class AstroSphere {
608
800
  // Rotation inertia
609
801
  if (this.mouseDown || Math.abs(this.inertiaX) > 0.02 || Math.abs(this.inertiaY) > 0.02) {
610
802
  cameraRotated = true;
611
- THETA = this.inertiaY;
612
- PHI = this.inertiaX;
613
- this.inertiaX *= 0.95;
614
- 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;
615
808
  this._camera.rotate(PHI, THETA);
616
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
+ }
617
814
  }
618
815
  else {
619
816
  this.inertiaY = 0;
@@ -672,7 +869,7 @@ class AstroSphere {
672
869
  this._activeHiPS?.draw(skyEntityDrawInput);
673
870
  }
674
871
  if (this._activeBaseLayer === 'xyz') {
675
- this._activeXYZ?.draw(skyEntityDrawInput);
872
+ this._activeXYZ2?.draw(skyEntityDrawInput);
676
873
  }
677
874
  this._healpixGrid.draw(skyEntityDrawInput);
678
875
  this._equatorialGrid.draw(skyEntityDrawInput);
@@ -694,14 +891,14 @@ class AstroSphere {
694
891
  });
695
892
  }
696
893
  this.activeCatalogues.forEach(cat => {
697
- const activeModelMatrix = this._activeHiPS?.getModelMatrix() ?? this._activeXYZ?.getModelMatrix();
894
+ const activeModelMatrix = this._activeHiPS?.getModelMatrix() ?? this._activeXYZ2?.getModelMatrix();
698
895
  if (activeModelMatrix) {
699
896
  cat.draw(activeModelMatrix, this.mouseHelper, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
700
897
  }
701
898
  });
702
899
  this.emitHoveredSourceIfChanged();
703
900
  this.activeFootprintSets.forEach(fst => {
704
- const activeModelMatrix = this._activeHiPS?.getModelMatrix() ?? this._activeXYZ?.getModelMatrix();
901
+ const activeModelMatrix = this._activeHiPS?.getModelMatrix() ?? this._activeXYZ2?.getModelMatrix();
705
902
  if (activeModelMatrix) {
706
903
  fst.draw(activeModelMatrix, this.mouseHelper, this._camera.getCameraMatrix(), this._perspectiveMatrixManager.pMatrix);
707
904
  }
@@ -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,8 +9,8 @@ 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 { XYZLayer } from './model/earth/XYZLayer.js';
13
- import type { WMTSLayerConfig, XYZDebugStats, XYZLayerConfig } from './model/earth/types.js';
12
+ import type { WMTSLayerConfig, XYZDebugStats, XYZLayerConfig } from './model/earth/XYZConfig.js';
13
+ import { XYZMap } from './model/earth/XYZMap.js';
14
14
  import { TerraPointSetGL } from './model/terra/TerraPointSetGL.js';
15
15
  import { TerraFootprintSetGL } from './model/terra/TerraFootprintSetGL.js';
16
16
  export declare class AstroViewer {
@@ -49,6 +49,9 @@ export declare class AstroViewer {
49
49
  getDefaultHiPSURL(): string;
50
50
  activateHiPS(hipsDescriptor: HiPSDescriptor): void;
51
51
  activateXYZ(config: XYZLayerConfig): void;
52
+ activateXYZ2(config: XYZLayerConfig & {
53
+ name?: string;
54
+ }): void;
52
55
  activateWMTS(config: WMTSLayerConfig): void;
53
56
  setXYZMaxConcurrentRequests(value: number): void;
54
57
  getXYZMaxConcurrentRequests(): number;
@@ -57,13 +60,14 @@ export declare class AstroViewer {
57
60
  changeColorMap(colorMapName: ColorMapName): void;
58
61
  changeCustomColorMap(colorMap: ColorMap): void;
59
62
  getActiveHiPS(): HiPS | null;
60
- getActiveXYZ(): XYZLayer | null;
63
+ getActiveXYZ(): XYZMap | null;
61
64
  setCamera(camera: Camera): void;
62
65
  setCameraPosition(pos: [number, number, number]): void;
63
66
  setCameraMatrix(viewMatrix: Float32Array): void;
64
67
  restoreAstroViewerState(detail: CameraChangedDetail, applyColorMap: boolean): void;
65
68
  getCurrentAstroViewerStatus(): CameraChangedDetail | null;
66
69
  goTo(raDeg: number, decDeg: number): void;
70
+ getActiveCoordinateMode(): 'equatorial' | 'galactic' | 'lonlat';
67
71
  getCenterCoordinates(): PointCoordinates | undefined;
68
72
  getCoordinatesFromMouse(): PointCoordinates | undefined;
69
73
  setModelMatrix(modelMatrix: ReadonlyMat4): void;
@@ -71,7 +75,16 @@ export declare class AstroViewer {
71
75
  isHealpixGridVisible(): boolean;
72
76
  toggleEquatorialGrid(): void;
73
77
  isEquatorialGridVisible(): boolean;
74
- 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;
75
88
  getFoVPolygon(): Point[];
76
89
  changeFoV(deg: number): void;
77
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';
@@ -6,6 +18,7 @@ import { FootprintSetGL } from './model/footprints/FootprintSetGL.js';
6
18
  import { bootSetup } from './Config.js';
7
19
  import ColorMaps from './model/ColorMaps.js';
8
20
  import { xyzTileRequestScheduler } from './model/earth/XYZTileRequestScheduler.js';
21
+ import { XYZMapDescriptor } from './model/earth/XYZMapDescriptor.js';
9
22
  import { TerraPointSetGL } from './model/terra/TerraPointSetGL.js';
10
23
  import { TerraFootprintSetGL } from './model/terra/TerraFootprintSetGL.js';
11
24
  // & {
@@ -116,6 +129,10 @@ export class AstroViewer {
116
129
  activateXYZ(config) {
117
130
  this.astroSphere.activateXYZ(config);
118
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
+ }
119
136
  activateWMTS(config) {
120
137
  this.astroSphere.activateWMTS(config);
121
138
  }
@@ -174,6 +191,9 @@ export class AstroViewer {
174
191
  // console.log(`AstroViewer.goTo goto(${raDeg}, ${decDeg})`)
175
192
  this.astroSphere.goTo(raDeg, decDeg);
176
193
  }
194
+ getActiveCoordinateMode() {
195
+ return this.astroSphere.getActiveCoordinateMode();
196
+ }
177
197
  getCenterCoordinates() {
178
198
  return this.astroSphere.getCentralPointCoordinates();
179
199
  }
@@ -200,6 +220,33 @@ export class AstroViewer {
200
220
  // return equatorialGridSingleton.isVisible()
201
221
  return this.astroSphere.equatorialGrid.isVisible();
202
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
+ }
203
250
  // FOV
204
251
  getFoV() {
205
252
  return this.astroSphere.getFoV();
package/lib-esm/Camera.js CHANGED
@@ -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
  /**
2
14
  * @author Fabrizio Giordano (Fab)
3
15
  */
@@ -44,10 +56,19 @@ class Camera {
44
56
  }
45
57
  goToPhiTheta(ptDeg) {
46
58
  const xyz = sphericalToCartesian(ptDeg.phi, ptDeg.theta, this.cam_pos[2]);
59
+ const targetDirection = vec3.normalize(vec3.create(), vec3.fromValues(xyz[0], xyz[1], xyz[2]));
60
+ const celestialNorth = vec3.fromValues(0.0, 0.0, 1.0);
61
+ const northProjection = vec3.scale(vec3.create(), targetDirection, vec3.dot(celestialNorth, targetDirection));
62
+ const cameraUp = vec3.subtract(vec3.create(), celestialNorth, northProjection);
63
+ if (vec3.length(cameraUp) < 1e-6) {
64
+ vec3.set(cameraUp, 0.0, 1.0, 0.0);
65
+ }
66
+ else {
67
+ vec3.normalize(cameraUp, cameraUp);
68
+ }
47
69
  let cameraMatrix = mat4.create();
48
70
  cameraMatrix = mat4.translate(cameraMatrix, cameraMatrix, vec3.fromValues(xyz[0], xyz[1], xyz[2]));
49
71
  const focusPoint = [0.0, 0.0, 0.0];
50
- const cameraUp = vec3.clone([0.0, 1.0, 0.0]);
51
72
  const cameraPos = [cameraMatrix[12], cameraMatrix[13], cameraMatrix[14]];
52
73
  cameraMatrix = mat4.targetTo(cameraMatrix, cameraPos, focusPoint, cameraUp);
53
74
  this.R = mat4.clone(cameraMatrix);
@@ -200,10 +221,6 @@ class Camera {
200
221
  const totRot = Math.sqrt(phi * phi + theta * theta);
201
222
  if (totRot === 0)
202
223
  return;
203
- // If both X and Y rotations are locked, nothing to do
204
- if (this.lockRotX && this.lockRotY) {
205
- return;
206
- }
207
224
  const pos = this.getCameraPosition();
208
225
  const dist2Center = Math.sqrt(pos[0] * pos[0] + pos[1] * pos[1] + pos[2] * pos[2]);
209
226
  const distanceFromSurface = Math.max(dist2Center - 1, 1e-6);
@@ -214,9 +231,8 @@ class Camera {
214
231
  const normalizedFoV = Math.min(1, this.FoV / 18);
215
232
  const fovFactor = 0.06 + 1.55 * Math.pow(normalizedFoV, 0.52);
216
233
  const usedRot = ((totRot * distanceFactor * fovFactor) / 1.9) * this.rotationSensitivity;
217
- // Build an axis from phi/theta, but zero components that are locked
218
- let axisX = this.lockRotX ? 0 : theta;
219
- let axisY = this.lockRotY ? 0 : phi;
234
+ let axisX = theta;
235
+ let axisY = phi;
220
236
  const axisLen = Math.sqrt(axisX * axisX + axisY * axisY);
221
237
  // If after locking we have no axis left, do nothing
222
238
  if (axisLen === 0) {
package/lib-esm/Config.js CHANGED
@@ -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 const hipsNodes = [
2
14
  "https://skies.esac.esa.int/",
3
15
  "https://alasky.cds.unistra.fr/",
package/lib-esm/Global.js CHANGED
@@ -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
  import { Healpix } from 'healpixjs';
3
15
  import { bootSetup } from './Config.js';