astro-viewer 3.0.0 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/dist/astroviewer.cjs +3082 -1410
  2. package/dist/astroviewer.js +1 -1
  3. package/dist/astroviewer.min.js +1 -1
  4. package/lib-esm/AstroSphere.d.ts +40 -16
  5. package/lib-esm/AstroSphere.js +359 -93
  6. package/lib-esm/AstroViewer.d.ts +20 -5
  7. package/lib-esm/AstroViewer.js +50 -0
  8. package/lib-esm/Camera.js +28 -14
  9. package/lib-esm/Config.d.ts +2 -0
  10. package/lib-esm/Config.js +15 -1
  11. package/lib-esm/Global.js +12 -0
  12. package/lib-esm/index.d.ts +8 -4
  13. package/lib-esm/index.js +17 -3
  14. package/lib-esm/model/AbstractSkyEntity.d.ts +1 -0
  15. package/lib-esm/model/AbstractSkyEntity.js +12 -0
  16. package/lib-esm/model/AstroGL.js +12 -0
  17. package/lib-esm/model/ColorMaps.js +12 -0
  18. package/lib-esm/model/MetadataColumn.js +12 -0
  19. package/lib-esm/model/MetadataManager.d.ts +1 -0
  20. package/lib-esm/model/MetadataManager.js +16 -1
  21. package/lib-esm/model/Point.d.ts +9 -1
  22. package/lib-esm/model/Point.js +30 -0
  23. package/lib-esm/model/Point2D.js +12 -0
  24. package/lib-esm/model/Source.js +12 -0
  25. package/lib-esm/model/SphereFoV.d.ts +30 -0
  26. package/lib-esm/model/SphereFoV.js +196 -0
  27. package/lib-esm/model/catalogues/CatalogueGL.js +12 -0
  28. package/lib-esm/model/catalogues/CatalogueProps.js +13 -1
  29. package/lib-esm/model/earth/{wmts/WMTSAdapter.d.ts → WMTSAdapter.d.ts} +2 -1
  30. package/lib-esm/model/earth/{wmts/WMTSAdapter.js → WMTSAdapter.js} +12 -0
  31. package/lib-esm/model/earth/XYZAnchestorTile.d.ts +17 -0
  32. package/lib-esm/model/earth/XYZAnchestorTile.js +87 -0
  33. package/lib-esm/model/earth/{types.d.ts → XYZConfig.d.ts} +6 -17
  34. package/lib-esm/model/earth/XYZConfig.js +13 -0
  35. package/lib-esm/model/earth/XYZFoVHelper.d.ts +12 -0
  36. package/lib-esm/model/earth/XYZFoVHelper.js +138 -0
  37. package/lib-esm/model/earth/XYZMap.d.ts +31 -0
  38. package/lib-esm/model/earth/XYZMap.js +208 -0
  39. package/lib-esm/model/earth/XYZMapDescriptor.d.ts +29 -0
  40. package/lib-esm/model/earth/XYZMapDescriptor.js +77 -0
  41. package/lib-esm/model/earth/XYZMeshBuilder.d.ts +1 -1
  42. package/lib-esm/model/earth/XYZMeshBuilder.js +13 -2
  43. package/lib-esm/model/earth/XYZTile.d.ts +20 -21
  44. package/lib-esm/model/earth/XYZTile.js +82 -102
  45. package/lib-esm/model/earth/XYZTileBuffer.d.ts +24 -26
  46. package/lib-esm/model/earth/XYZTileBuffer.js +60 -70
  47. package/lib-esm/model/earth/XYZTileRequestScheduler.d.ts +1 -1
  48. package/lib-esm/model/earth/XYZTileRequestScheduler.js +13 -2
  49. package/lib-esm/model/earth/XYZTypes.d.ts +24 -0
  50. package/lib-esm/model/earth/XYZTypes.js +13 -0
  51. package/lib-esm/model/earth/XYZVisibleTilesManager.d.ts +16 -27
  52. package/lib-esm/model/earth/XYZVisibleTilesManager.js +61 -192
  53. package/lib-esm/model/footprints/Footprint.d.ts +4 -1
  54. package/lib-esm/model/footprints/Footprint.js +30 -2
  55. package/lib-esm/model/footprints/FootprintProps.js +13 -1
  56. package/lib-esm/model/footprints/FootprintSetGL.d.ts +4 -2
  57. package/lib-esm/model/footprints/FootprintSetGL.js +21 -6
  58. package/lib-esm/model/grid/EquatorialGrid.d.ts +2 -1
  59. package/lib-esm/model/grid/EquatorialGrid.js +30 -12
  60. package/lib-esm/model/grid/GridTextHelper.d.ts +10 -19
  61. package/lib-esm/model/grid/GridTextHelper.js +70 -67
  62. package/lib-esm/model/grid/HealpixGrid.d.ts +4 -4
  63. package/lib-esm/model/grid/HealpixGrid.js +24 -8
  64. package/lib-esm/model/grid/LonLatGrid.d.ts +43 -0
  65. package/lib-esm/model/grid/LonLatGrid.js +309 -0
  66. package/lib-esm/model/hips/AllSky.js +12 -0
  67. package/lib-esm/model/hips/AncestorTile.js +12 -0
  68. package/lib-esm/model/hips/FoVHelper.d.ts +5 -2
  69. package/lib-esm/model/hips/FoVHelper.js +52 -3
  70. package/lib-esm/model/hips/HiPS.d.ts +2 -0
  71. package/lib-esm/model/hips/HiPS.js +38 -14
  72. package/lib-esm/model/hips/HiPSConfig.d.ts +13 -0
  73. package/lib-esm/model/hips/HiPSDescriptor.js +12 -0
  74. package/lib-esm/model/hips/Tile.d.ts +1 -0
  75. package/lib-esm/model/hips/Tile.js +15 -0
  76. package/lib-esm/model/hips/TileBuffer.d.ts +5 -0
  77. package/lib-esm/model/hips/TileBuffer.js +61 -0
  78. package/lib-esm/model/hips/VisibleTilesManager.js +12 -0
  79. package/lib-esm/model/tap/TapMetadata.js +12 -0
  80. package/lib-esm/model/tap/TapMetadataList.js +12 -0
  81. package/lib-esm/model/tap/TapRepo.js +12 -0
  82. package/lib-esm/model/terra/TerraFootprintSetGL.d.ts +6 -0
  83. package/lib-esm/model/terra/TerraFootprintSetGL.js +54 -0
  84. package/lib-esm/model/terra/TerraPointSetGL.js +12 -0
  85. package/lib-esm/services/SesameService.js +11 -3
  86. package/lib-esm/services/helpers.js +12 -0
  87. package/lib-esm/services/hipsNodeService.js +12 -0
  88. package/lib-esm/services/parse-hipslist.js +12 -0
  89. package/lib-esm/services/queryCatalogueByFoV.js +13 -1
  90. package/lib-esm/services/queryFootprintSetByFov.js +13 -1
  91. package/lib-esm/services/tapRepoService.js +13 -1
  92. package/lib-esm/services/types.js +12 -0
  93. package/lib-esm/shader/CatalogueShaderProgram.js +12 -0
  94. package/lib-esm/shader/FootprintShaderProgram.js +12 -0
  95. package/lib-esm/shader/GridShaderManager.js +12 -0
  96. package/lib-esm/shader/HiPSShaderProgram.js +12 -0
  97. package/lib-esm/shader/ShaderManager.js +12 -1
  98. package/lib-esm/shader/ShaderManagerMutliHiPS.js +12 -1
  99. package/lib-esm/shader/XYZShaderProgram.d.ts +14 -1
  100. package/lib-esm/shader/XYZShaderProgram.js +127 -2
  101. package/lib-esm/utils/CoordsType.d.ts +2 -1
  102. package/lib-esm/utils/CoordsType.js +13 -0
  103. package/lib-esm/utils/FoVUtils.js +12 -0
  104. package/lib-esm/utils/GeoJSONParser.d.ts +19 -0
  105. package/lib-esm/utils/GeoJSONParser.js +112 -0
  106. package/lib-esm/utils/GeomUtils.js +12 -0
  107. package/lib-esm/utils/MouseHelper.js +12 -0
  108. package/lib-esm/utils/PerspectiveMatrixManager.js +16 -1
  109. package/lib-esm/utils/RayPickingUtils.js +12 -0
  110. package/lib-esm/utils/STCSParser.d.ts +7 -3
  111. package/lib-esm/utils/STCSParser.js +27 -9
  112. package/lib-esm/utils/ShaderUtility.js +13 -1
  113. package/lib-esm/utils/Utils.js +12 -0
  114. package/lib-esm/utils/XYZRayPickingUtils.d.ts +27 -0
  115. package/lib-esm/utils/XYZRayPickingUtils.js +226 -0
  116. package/package.json +2 -2
  117. package/lib-esm/model/FoV.d.ts +0 -62
  118. package/lib-esm/model/FoV.js +0 -274
  119. package/lib-esm/model/earth/XYZAncestorMeshCache.d.ts +0 -10
  120. package/lib-esm/model/earth/XYZAncestorMeshCache.js +0 -31
  121. package/lib-esm/model/earth/XYZLayer.d.ts +0 -31
  122. package/lib-esm/model/earth/XYZLayer.js +0 -231
  123. package/lib-esm/model/earth/XYZTileProvider.d.ts +0 -29
  124. package/lib-esm/model/earth/XYZTileProvider.js +0 -187
  125. package/lib-esm/model/earth/wmts/types.d.ts +0 -1
  126. package/lib-esm/model/earth/wmts/types.js +0 -1
  127. /package/lib-esm/model/{earth/types.js → hips/HiPSConfig.js} +0 -0
@@ -1,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)
@@ -0,0 +1,19 @@
1
+ import { Point } from '../model/Point.js';
2
+ export type GeoJSONProperties = Record<string, unknown>;
3
+ export interface ParsedGeoJSONFeature {
4
+ id?: string | number;
5
+ geometryType: 'Polygon' | 'MultiPolygon';
6
+ properties: GeoJSONProperties;
7
+ polygons: Point[][];
8
+ }
9
+ declare class GeoJSONParser {
10
+ static isGeoJSON(value: unknown): boolean;
11
+ static parseGeoJSON(value: unknown): ParsedGeoJSONFeature[];
12
+ private static parseFeature;
13
+ private static parseGeometry;
14
+ private static parseMultiPolygonCoordinates;
15
+ private static parsePolygonCoordinates;
16
+ private static parseLinearRing;
17
+ private static parsePosition;
18
+ }
19
+ export default GeoJSONParser;
@@ -0,0 +1,112 @@
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 { CoordsType } from './CoordsType.js';
14
+ import { Point } from '../model/Point.js';
15
+ class GeoJSONParser {
16
+ static isGeoJSON(value) {
17
+ if (!value || typeof value !== 'object')
18
+ return false;
19
+ const type = value.type;
20
+ return type === 'FeatureCollection'
21
+ || type === 'Feature'
22
+ || type === 'Polygon'
23
+ || type === 'MultiPolygon'
24
+ || type === 'GeometryCollection';
25
+ }
26
+ static parseGeoJSON(value) {
27
+ if (!value || typeof value !== 'object') {
28
+ throw new Error('GeoJSON root must be an object');
29
+ }
30
+ const obj = value;
31
+ if (obj.type === 'FeatureCollection') {
32
+ if (!Array.isArray(obj.features))
33
+ throw new Error('GeoJSON FeatureCollection has no features array');
34
+ return obj.features.flatMap((feature) => GeoJSONParser.parseFeature(feature));
35
+ }
36
+ if (obj.type === 'Feature')
37
+ return GeoJSONParser.parseFeature(obj);
38
+ if (obj.type === 'Polygon' || obj.type === 'MultiPolygon' || obj.type === 'GeometryCollection') {
39
+ return GeoJSONParser.parseGeometry(obj, {});
40
+ }
41
+ throw new Error(`Unsupported GeoJSON type: ${obj.type ?? 'unknown'}`);
42
+ }
43
+ static parseFeature(value) {
44
+ if (!value || typeof value !== 'object')
45
+ throw new Error('GeoJSON feature must be an object');
46
+ const feature = value;
47
+ if (feature.type !== 'Feature')
48
+ throw new Error('GeoJSON feature has invalid type');
49
+ if (!feature.geometry)
50
+ return [];
51
+ return GeoJSONParser.parseGeometry(feature.geometry, feature.properties ?? {}, feature.id);
52
+ }
53
+ static parseGeometry(geometry, properties, id) {
54
+ if (geometry.type === 'Polygon') {
55
+ return [{
56
+ id,
57
+ geometryType: 'Polygon',
58
+ properties,
59
+ polygons: GeoJSONParser.parsePolygonCoordinates(geometry.coordinates),
60
+ }];
61
+ }
62
+ if (geometry.type === 'MultiPolygon') {
63
+ return [{
64
+ id,
65
+ geometryType: 'MultiPolygon',
66
+ properties,
67
+ polygons: GeoJSONParser.parseMultiPolygonCoordinates(geometry.coordinates),
68
+ }];
69
+ }
70
+ if (geometry.type === 'GeometryCollection') {
71
+ if (!Array.isArray(geometry.geometries))
72
+ return [];
73
+ return geometry.geometries.flatMap((child) => GeoJSONParser.parseGeometry(child, properties, id));
74
+ }
75
+ return [];
76
+ }
77
+ static parseMultiPolygonCoordinates(coordinates) {
78
+ if (!Array.isArray(coordinates))
79
+ throw new Error('GeoJSON MultiPolygon coordinates must be an array');
80
+ return coordinates.flatMap((polygonCoordinates) => GeoJSONParser.parsePolygonCoordinates(polygonCoordinates));
81
+ }
82
+ static parsePolygonCoordinates(coordinates) {
83
+ if (!Array.isArray(coordinates))
84
+ throw new Error('GeoJSON Polygon coordinates must be an array');
85
+ return coordinates
86
+ .map((ring) => GeoJSONParser.parseLinearRing(ring))
87
+ .filter((ring) => ring.length >= 3);
88
+ }
89
+ static parseLinearRing(ring) {
90
+ if (!Array.isArray(ring))
91
+ throw new Error('GeoJSON linear ring must be an array');
92
+ const points = ring.map((position) => GeoJSONParser.parsePosition(position));
93
+ if (points.length > 1) {
94
+ const first = points[0];
95
+ const last = points[points.length - 1];
96
+ if (first.lonDeg === last.lonDeg && first.latDeg === last.latDeg)
97
+ points.pop();
98
+ }
99
+ return points;
100
+ }
101
+ static parsePosition(position) {
102
+ if (!Array.isArray(position) || position.length < 2) {
103
+ throw new Error('GeoJSON position must be [longitude, latitude]');
104
+ }
105
+ const [lonDeg, latDeg] = position;
106
+ if (!Number.isFinite(lonDeg) || !Number.isFinite(latDeg)) {
107
+ throw new Error('GeoJSON position contains non-finite longitude/latitude');
108
+ }
109
+ return new Point({ lonDeg, latDeg }, CoordsType.GEOGRAPHIC);
110
+ }
111
+ }
112
+ export default GeoJSONParser;
@@ -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 { Point } from "../model/Point.js";
2
14
  import Point2D from "../model/Point2D.js";
3
15
  import { CoordsType } from "./CoordsType.js";
@@ -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
  */
@@ -1,4 +1,17 @@
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 { mat4 } from "gl-matrix";
14
+ import { bootSetup } from "../Config.js";
2
15
  export class PerspectiveMatrixManager {
3
16
  _pMatrix;
4
17
  _aspectRatio = 1;
@@ -29,7 +42,9 @@ export class PerspectiveMatrixManager {
29
42
  const cf = c2 * Math.sin(beta);
30
43
  farPlane = cf > 0 ? cf : r;
31
44
  }
32
- mat4.perspective(p, (fovDeg * Math.PI) / 180, this._aspectRatio, nearPlane, farPlane);
45
+ const effectiveFovDeg = insideSphere ? bootSetup.inside_camera_fov_deg : fovDeg;
46
+ const effectiveNearPlane = insideSphere ? Math.max(nearPlane, 0.001) : nearPlane;
47
+ mat4.perspective(p, (effectiveFovDeg * Math.PI) / 180, this._aspectRatio, effectiveNearPlane, farPlane);
33
48
  this._pMatrix = p;
34
49
  return p;
35
50
  }
@@ -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
  */
@@ -2,14 +2,18 @@
2
2
  * @author Fabrizio Giordano (Fab77)
3
3
  */
4
4
  import { Point } from "../model/Point.js";
5
+ import { CoordsType } from "./CoordsType.js";
5
6
  export interface STCSParseResult {
6
7
  totpoints: number;
7
8
  polygons: Point[][];
8
9
  }
10
+ export interface STCSParseOptions {
11
+ coordsType?: CoordsType.ASTRO | CoordsType.GEOGRAPHIC;
12
+ }
9
13
  declare class STCSParser {
10
- static parseSTCS(stcs: string): STCSParseResult;
14
+ static parseSTCS(stcs: string, options?: STCSParseOptions): STCSParseResult;
11
15
  static cleanStcs(stcs: string): string;
12
- static parsePolygon(stcs: string): STCSParseResult;
13
- static parseCircle(stcs: string): STCSParseResult;
16
+ static parsePolygon(stcs: string, options?: STCSParseOptions): STCSParseResult;
17
+ static parseCircle(stcs: string, options?: STCSParseOptions): STCSParseResult;
14
18
  }
15
19
  export default STCSParser;
@@ -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 (Fab77)
3
15
  */
@@ -5,15 +17,15 @@ import { Point } from "../model/Point.js";
5
17
  import { CoordsType } from "./CoordsType.js";
6
18
  import global from "../Global.js";
7
19
  class STCSParser {
8
- static parseSTCS(stcs) {
20
+ static parseSTCS(stcs, options = {}) {
9
21
  const stcsParsed = STCSParser.cleanStcs(stcs);
10
22
  let totPoints = 0;
11
23
  const polygons = [];
12
24
  if (stcsParsed.includes("POLYGON")) {
13
- return STCSParser.parsePolygon(stcsParsed);
25
+ return STCSParser.parsePolygon(stcsParsed, options);
14
26
  }
15
27
  else if (stcsParsed.includes("CIRCLE")) {
16
- return STCSParser.parseCircle(stcsParsed);
28
+ return STCSParser.parseCircle(stcsParsed, options);
17
29
  }
18
30
  else {
19
31
  console.warn("STCS not recognised");
@@ -36,10 +48,11 @@ class STCSParser {
36
48
  s = s.replace(/ {2,}/g, ' ').trim();
37
49
  return s;
38
50
  }
39
- static parsePolygon(stcs) {
51
+ static parsePolygon(stcs, options = {}) {
40
52
  let totPoints = 0;
41
53
  const polygons = [];
42
54
  const MAX_DECIMALS = global.MAX_DECIMALS ?? 12;
55
+ const coordsType = options.coordsType ?? CoordsType.ASTRO;
43
56
  const polys = stcs.split("POLYGON ");
44
57
  for (let i = 1; i < polys.length; i++) {
45
58
  const currPoly = [];
@@ -54,9 +67,11 @@ class STCSParser {
54
67
  }
55
68
  if (points.length > 2) {
56
69
  for (let p = 0; p < points.length - 1; p += 2) {
57
- const raDeg = Number(parseFloat(points[p]).toFixed(MAX_DECIMALS));
58
- const decDeg = Number(parseFloat(points[p + 1]).toFixed(MAX_DECIMALS));
59
- const point = new Point({ raDeg, decDeg }, CoordsType.ASTRO);
70
+ const xDeg = Number(parseFloat(points[p]).toFixed(MAX_DECIMALS));
71
+ const yDeg = Number(parseFloat(points[p + 1]).toFixed(MAX_DECIMALS));
72
+ const point = coordsType === CoordsType.GEOGRAPHIC
73
+ ? new Point({ lonDeg: xDeg, latDeg: yDeg }, CoordsType.GEOGRAPHIC)
74
+ : new Point({ raDeg: xDeg, decDeg: yDeg }, CoordsType.ASTRO);
60
75
  currPoly.push(point);
61
76
  totPoints += 1;
62
77
  }
@@ -66,9 +81,10 @@ class STCSParser {
66
81
  return { totpoints: totPoints, polygons };
67
82
  }
68
83
  // Example format: "CIRCLE ICRS 8.739685 4.38147 0.027833"
69
- static parseCircle(stcs) {
84
+ static parseCircle(stcs, options = {}) {
70
85
  let totPoints = 0;
71
86
  const polygons = [];
87
+ const coordsType = options.coordsType ?? CoordsType.ASTRO;
72
88
  const polys = stcs.split("CIRCLE ");
73
89
  for (let i = 1; i < polys.length; i++) {
74
90
  const currPoly = [];
@@ -83,7 +99,9 @@ class STCSParser {
83
99
  for (let p = npoints; p > 0; p--) {
84
100
  const curra = radius * Math.cos(p * alpha) + ra;
85
101
  const curdec = radius * Math.sin(p * alpha) + dec;
86
- const point = new Point({ raDeg: curra, decDeg: curdec }, CoordsType.ASTRO);
102
+ const point = coordsType === CoordsType.GEOGRAPHIC
103
+ ? new Point({ lonDeg: curra, latDeg: curdec }, CoordsType.GEOGRAPHIC)
104
+ : new Point({ raDeg: curra, decDeg: curdec }, CoordsType.ASTRO);
87
105
  currPoly.push(point);
88
106
  totPoints += 1;
89
107
  }
@@ -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 global from "../Global.js";
2
15
  // // import ShaderManager from "../shader/ShaderManager.js";
3
- export {};
4
16
  // // type GL = WebGLRenderingContext | WebGL2RenderingContext;
5
17
  // class ShaderUtility {
6
18
  // private lastUsedProgram: WebGLProgram | null = null;
@@ -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
  */
@@ -0,0 +1,27 @@
1
+ import { ReadonlyMat4, ReadonlyVec3, vec3 } from 'gl-matrix';
2
+ import Camera from '../Camera.js';
3
+ import { AbstractSkyEntity } from '../model/AbstractSkyEntity.js';
4
+ import type { XYZTileCoord } from '../model/earth/XYZTypes.js';
5
+ export type XYZLonLat = {
6
+ lonDeg: number;
7
+ latDeg: number;
8
+ };
9
+ declare class XYZRayPickingUtils {
10
+ static getRayFromMouse(mouseX: number, mouseY: number, pMatrix: ReadonlyMat4, webgl: WebGL2RenderingContext, vMatrix: ReadonlyMat4): vec3;
11
+ static raySphere(rayOrigWorld: ReadonlyVec3, rayDirectionWorld: ReadonlyVec3, sphere: Pick<AbstractSkyEntity, 'center' | 'radius'>): number;
12
+ static getIntersectionPointWithModel(mouseX: number, mouseY: number, xyzModel: AbstractSkyEntity, webgl: WebGL2RenderingContext, camera: Camera, pMatrix: ReadonlyMat4): [number, number, number] | null;
13
+ static getLonLatFromMouse(mouseX: number, mouseY: number, xyzModel: AbstractSkyEntity, webgl: WebGL2RenderingContext, camera: Camera, pMatrix: ReadonlyMat4): XYZLonLat | null;
14
+ static getTileFromMouse(mouseX: number, mouseY: number, z: number, xyzModel: AbstractSkyEntity, webgl: WebGL2RenderingContext, camera: Camera, pMatrix: ReadonlyMat4): XYZTileCoord | null;
15
+ static getVisibleTilesFromViewport(z: number, xyzModel: AbstractSkyEntity, webgl: WebGL2RenderingContext, camera: Camera, pMatrix: ReadonlyMat4, sampleCount?: number, padding?: number): XYZTileCoord[];
16
+ static modelPointToLonLat(point: Readonly<[number, number, number]>): XYZLonLat;
17
+ static lonLatToTile(lonDeg: number, latDeg: number, z: number): XYZTileCoord;
18
+ static getNeighborTiles(tile: XYZTileCoord, ring?: number): XYZTileCoord[];
19
+ static deduplicateTiles(tiles: XYZTileCoord[]): XYZTileCoord[];
20
+ private static fillSmallTileGaps;
21
+ private static latToTileY;
22
+ private static isMercatorLatitude;
23
+ private static wrapTileX;
24
+ private static clampTileY;
25
+ private static mat4MultiplyVec4;
26
+ }
27
+ export default XYZRayPickingUtils;
@@ -0,0 +1,226 @@
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
+ /**
14
+ * Ray picking helpers for XYZ/WebMercator maps.
15
+ *
16
+ * Unlike RayPickingUtils, this module has no HEALPix dependency. It intersects
17
+ * screen rays with an XYZ sphere, converts the hit to lon/lat, then maps that
18
+ * position to XYZ z/x/y tile coordinates.
19
+ */
20
+ 'use strict';
21
+ import { mat4, vec3 } from 'gl-matrix';
22
+ const MAX_MERCATOR_LAT = 85.0511287798066;
23
+ class XYZRayPickingUtils {
24
+ static getRayFromMouse(mouseX, mouseY, pMatrix, webgl, vMatrix) {
25
+ const gl = webgl;
26
+ const canvas = gl.canvas;
27
+ const rect = canvas.getBoundingClientRect();
28
+ const x = (2.0 * mouseX) / rect.width - 1.0;
29
+ const y = 1.0 - (2.0 * mouseY) / rect.height;
30
+ const rayClip = [x, y, -1.0, 1.0];
31
+ const pInv = mat4.create();
32
+ mat4.invert(pInv, pMatrix);
33
+ const rayEye4 = [0, 0, 0, 0];
34
+ XYZRayPickingUtils.mat4MultiplyVec4(pInv, rayClip, rayEye4);
35
+ const rayEye = [rayEye4[0], rayEye4[1], -1.0, 0.0];
36
+ const vInv = mat4.create();
37
+ mat4.invert(vInv, vMatrix);
38
+ const rayWorld4 = [0, 0, 0, 0];
39
+ XYZRayPickingUtils.mat4MultiplyVec4(vInv, rayEye, rayWorld4);
40
+ const rayWorld = vec3.fromValues(rayWorld4[0], rayWorld4[1], rayWorld4[2]);
41
+ vec3.normalize(rayWorld, rayWorld);
42
+ return rayWorld;
43
+ }
44
+ static raySphere(rayOrigWorld, rayDirectionWorld, sphere) {
45
+ let intersectionDistance = -1;
46
+ const L = vec3.create();
47
+ vec3.subtract(L, rayOrigWorld, sphere.center);
48
+ const b = vec3.dot(rayDirectionWorld, L);
49
+ const c = vec3.dot(L, L) - sphere.radius * sphere.radius;
50
+ const disc = b * b - c;
51
+ if (disc > 0.0) {
52
+ const s = Math.sqrt(disc);
53
+ const ta = -b + s;
54
+ const tb = -b - s;
55
+ if (ta >= 0.0 || tb >= 0.0) {
56
+ intersectionDistance = tb < 0.0 ? ta : Math.min(ta, tb);
57
+ }
58
+ }
59
+ else if (disc === 0.0) {
60
+ const t = -b;
61
+ if (t >= 0.0) {
62
+ intersectionDistance = t;
63
+ }
64
+ }
65
+ return intersectionDistance;
66
+ }
67
+ static getIntersectionPointWithModel(mouseX, mouseY, xyzModel, webgl, camera, pMatrix) {
68
+ const vMatrix = camera.getCameraMatrix();
69
+ const rayWorld = XYZRayPickingUtils.getRayFromMouse(mouseX, mouseY, pMatrix, webgl, vMatrix);
70
+ const t = XYZRayPickingUtils.raySphere(camera.getCameraPosition(), rayWorld, xyzModel);
71
+ if (t < 0) {
72
+ return null;
73
+ }
74
+ const worldHit = vec3.create();
75
+ vec3.scale(worldHit, rayWorld, t);
76
+ vec3.add(worldHit, camera.getCameraPosition(), worldHit);
77
+ const worldHit4 = [worldHit[0], worldHit[1], worldHit[2], 1.0];
78
+ const modelHit4 = [0, 0, 0, 0];
79
+ XYZRayPickingUtils.mat4MultiplyVec4(xyzModel.getModelMatrixInverse(), worldHit4, modelHit4);
80
+ return [modelHit4[0], modelHit4[1], modelHit4[2]];
81
+ }
82
+ static getLonLatFromMouse(mouseX, mouseY, xyzModel, webgl, camera, pMatrix) {
83
+ const hit = XYZRayPickingUtils.getIntersectionPointWithModel(mouseX, mouseY, xyzModel, webgl, camera, pMatrix);
84
+ return hit ? XYZRayPickingUtils.modelPointToLonLat(hit) : null;
85
+ }
86
+ static getTileFromMouse(mouseX, mouseY, z, xyzModel, webgl, camera, pMatrix) {
87
+ const lonLat = XYZRayPickingUtils.getLonLatFromMouse(mouseX, mouseY, xyzModel, webgl, camera, pMatrix);
88
+ if (!lonLat || !XYZRayPickingUtils.isMercatorLatitude(lonLat.latDeg)) {
89
+ return null;
90
+ }
91
+ return XYZRayPickingUtils.lonLatToTile(lonLat.lonDeg, lonLat.latDeg, z);
92
+ }
93
+ static getVisibleTilesFromViewport(z, xyzModel, webgl, camera, pMatrix, sampleCount = 9, padding = 2) {
94
+ const gl = webgl;
95
+ const canvas = gl.canvas;
96
+ const rect = canvas.getBoundingClientRect();
97
+ const samples = Math.max(2, Math.floor(sampleCount));
98
+ const edgeSamples = Math.max(samples * 2 + 1, 21);
99
+ const safePadding = Math.max(0, Math.floor(padding));
100
+ const tiles = [];
101
+ const addSample = (x, y) => {
102
+ const tile = XYZRayPickingUtils.getTileFromMouse(x, y, z, xyzModel, webgl, camera, pMatrix);
103
+ if (!tile)
104
+ return;
105
+ tiles.push(tile);
106
+ tiles.push(...XYZRayPickingUtils.getNeighborTiles(tile, safePadding));
107
+ };
108
+ for (let iy = 0; iy < samples; iy++) {
109
+ const y = samples === 1 ? rect.height / 2 : (iy / (samples - 1)) * rect.height;
110
+ for (let ix = 0; ix < samples; ix++) {
111
+ const x = samples === 1 ? rect.width / 2 : (ix / (samples - 1)) * rect.width;
112
+ addSample(x, y);
113
+ }
114
+ }
115
+ for (let i = 0; i < edgeSamples; i++) {
116
+ const t = edgeSamples === 1 ? 0.5 : i / (edgeSamples - 1);
117
+ const x = t * rect.width;
118
+ const y = t * rect.height;
119
+ addSample(x, 0);
120
+ addSample(x, rect.height);
121
+ addSample(0, y);
122
+ addSample(rect.width, y);
123
+ }
124
+ return XYZRayPickingUtils.fillSmallTileGaps(XYZRayPickingUtils.deduplicateTiles(tiles));
125
+ }
126
+ static modelPointToLonLat(point) {
127
+ const [x, y, z] = point;
128
+ const len = Math.hypot(x, y, z);
129
+ if (!Number.isFinite(len) || len === 0) {
130
+ return { lonDeg: 0, latDeg: 0 };
131
+ }
132
+ const lonDeg = (Math.atan2(y, x) * 180) / Math.PI;
133
+ const latDeg = (Math.asin(Math.max(-1, Math.min(1, z / len))) * 180) / Math.PI;
134
+ return { lonDeg, latDeg };
135
+ }
136
+ static lonLatToTile(lonDeg, latDeg, z) {
137
+ const zoom = Math.max(0, Math.floor(z));
138
+ const dim = 2 ** zoom;
139
+ const x = Math.floor(((lonDeg + 180) / 360) * dim);
140
+ const y = Math.floor(XYZRayPickingUtils.latToTileY(latDeg, zoom));
141
+ return {
142
+ z: zoom,
143
+ x: XYZRayPickingUtils.wrapTileX(x, dim),
144
+ y: XYZRayPickingUtils.clampTileY(y, dim),
145
+ };
146
+ }
147
+ static getNeighborTiles(tile, ring = 1) {
148
+ const dim = 2 ** tile.z;
149
+ const tiles = [];
150
+ const safeRing = Math.max(0, Math.floor(ring));
151
+ for (let dx = -safeRing; dx <= safeRing; dx++) {
152
+ for (let dy = -safeRing; dy <= safeRing; dy++) {
153
+ tiles.push({
154
+ z: tile.z,
155
+ x: XYZRayPickingUtils.wrapTileX(tile.x + dx, dim),
156
+ y: XYZRayPickingUtils.clampTileY(tile.y + dy, dim),
157
+ });
158
+ }
159
+ }
160
+ return XYZRayPickingUtils.deduplicateTiles(tiles);
161
+ }
162
+ static deduplicateTiles(tiles) {
163
+ const map = new Map();
164
+ for (const tile of tiles) {
165
+ map.set(`${tile.z}/${tile.x}/${tile.y}`, tile);
166
+ }
167
+ return Array.from(map.values());
168
+ }
169
+ static fillSmallTileGaps(tiles) {
170
+ if (tiles.length === 0) {
171
+ return tiles;
172
+ }
173
+ const zoom = tiles[0].z;
174
+ const dim = 2 ** zoom;
175
+ const map = new Map();
176
+ const key = (tile) => `${tile.z}/${tile.x}/${tile.y}`;
177
+ const add = (tile) => {
178
+ map.set(key(tile), tile);
179
+ };
180
+ const has = (x, y) => (y >= 0 && y < dim && map.has(`${zoom}/${XYZRayPickingUtils.wrapTileX(x, dim)}/${y}`));
181
+ for (const tile of tiles) {
182
+ add(tile);
183
+ }
184
+ for (const tile of tiles) {
185
+ const x = tile.x;
186
+ const y = tile.y;
187
+ if (has(x - 2, y) && !has(x - 1, y)) {
188
+ add({ z: zoom, x: XYZRayPickingUtils.wrapTileX(x - 1, dim), y });
189
+ }
190
+ if (has(x + 2, y) && !has(x + 1, y)) {
191
+ add({ z: zoom, x: XYZRayPickingUtils.wrapTileX(x + 1, dim), y });
192
+ }
193
+ if (has(x, y - 2) && !has(x, y - 1)) {
194
+ add({ z: zoom, x, y: y - 1 });
195
+ }
196
+ if (has(x, y + 2) && !has(x, y + 1)) {
197
+ add({ z: zoom, x, y: y + 1 });
198
+ }
199
+ }
200
+ return Array.from(map.values());
201
+ }
202
+ static latToTileY(latDeg, z) {
203
+ const lat = Math.max(-MAX_MERCATOR_LAT, Math.min(MAX_MERCATOR_LAT, latDeg));
204
+ const latRad = (lat * Math.PI) / 180;
205
+ const dim = 2 ** z;
206
+ return ((1 - Math.asinh(Math.tan(latRad)) / Math.PI) / 2) * dim;
207
+ }
208
+ static isMercatorLatitude(latDeg) {
209
+ return Math.abs(latDeg) <= MAX_MERCATOR_LAT;
210
+ }
211
+ static wrapTileX(x, dim) {
212
+ return ((x % dim) + dim) % dim;
213
+ }
214
+ static clampTileY(y, dim) {
215
+ return Math.max(0, Math.min(dim - 1, y));
216
+ }
217
+ static mat4MultiplyVec4(a, b, out) {
218
+ const d = b[0], e = b[1], g = b[2], w = b[3];
219
+ out[0] = a[0] * d + a[4] * e + a[8] * g + a[12] * w;
220
+ out[1] = a[1] * d + a[5] * e + a[9] * g + a[13] * w;
221
+ out[2] = a[2] * d + a[6] * e + a[10] * g + a[14] * w;
222
+ out[3] = a[3] * d + a[7] * e + a[11] * g + a[15] * w;
223
+ return out;
224
+ }
225
+ }
226
+ export default XYZRayPickingUtils;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "astro-viewer",
3
3
  "description": "Astrobrowser 3d engine.",
4
- "version": "3.0.0",
4
+ "version": "3.2.0",
5
5
  "keywords": [
6
6
  "HiPS",
7
7
  "HiPS cutout",
@@ -49,7 +49,7 @@
49
49
  "prod": "npm run clean && tsc -p tsconfig.build.json && webpack --mode=production && npm run web",
50
50
  "build": "npm run clean && tsc -p tsconfig.build.json && webpack --mode=production",
51
51
  "web": "cp dist/* public/javascripts/; cp -R src/html/javascripts/ public/javascripts/; cp -R src/html/css/ public/css/; cp src/html/*.html public/",
52
- "start-server": "python3 -m http.server 8080 -d public",
52
+ "start-server": "npm run web; python3 -m http.server 8080 -d public",
53
53
  "all": "npm run clean; npm run prod; npm run start-server",
54
54
  "prepublishOnly": "npm run build"
55
55
  },