astro-viewer 3.7.0 → 3.9.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 (56) hide show
  1. package/README.md +325 -222
  2. package/dist/astroviewer.cjs +654 -108
  3. package/dist/astroviewer.cjs.map +1 -1
  4. package/dist/astroviewer.js +655 -108
  5. package/dist/astroviewer.js.map +1 -1
  6. package/dist/astroviewer.min.js +1 -1
  7. package/dist/astroviewer.min.js.map +1 -1
  8. package/lib-esm/AstroSphere.d.ts +13 -1
  9. package/lib-esm/AstroSphere.d.ts.map +1 -1
  10. package/lib-esm/AstroSphere.js +94 -5
  11. package/lib-esm/AstroSphere.js.map +1 -1
  12. package/lib-esm/AstroViewer.d.ts +11 -0
  13. package/lib-esm/AstroViewer.d.ts.map +1 -1
  14. package/lib-esm/AstroViewer.js +37 -2
  15. package/lib-esm/AstroViewer.js.map +1 -1
  16. package/lib-esm/index.d.ts +1 -0
  17. package/lib-esm/index.d.ts.map +1 -1
  18. package/lib-esm/index.js +0 -1
  19. package/lib-esm/index.js.map +1 -1
  20. package/lib-esm/model/grid/HealpixGrid.d.ts.map +1 -1
  21. package/lib-esm/model/grid/HealpixGrid.js +7 -1
  22. package/lib-esm/model/grid/HealpixGrid.js.map +1 -1
  23. package/lib-esm/model/hips/AllSky.d.ts +0 -1
  24. package/lib-esm/model/hips/AllSky.d.ts.map +1 -1
  25. package/lib-esm/model/hips/AllSky.js +11 -32
  26. package/lib-esm/model/hips/AllSky.js.map +1 -1
  27. package/lib-esm/model/hips/AncestorTile.d.ts.map +1 -1
  28. package/lib-esm/model/hips/AncestorTile.js +10 -27
  29. package/lib-esm/model/hips/AncestorTile.js.map +1 -1
  30. package/lib-esm/model/hips/FitsTileLoader.d.ts +4 -0
  31. package/lib-esm/model/hips/FitsTileLoader.d.ts.map +1 -1
  32. package/lib-esm/model/hips/FitsTileLoader.js +39 -1
  33. package/lib-esm/model/hips/FitsTileLoader.js.map +1 -1
  34. package/lib-esm/model/hips/HiPS.d.ts +31 -0
  35. package/lib-esm/model/hips/HiPS.d.ts.map +1 -1
  36. package/lib-esm/model/hips/HiPS.js +193 -0
  37. package/lib-esm/model/hips/HiPS.js.map +1 -1
  38. package/lib-esm/model/hips/HiPSCoverage.d.ts +6 -1
  39. package/lib-esm/model/hips/HiPSCoverage.d.ts.map +1 -1
  40. package/lib-esm/model/hips/HiPSCoverage.js +20 -2
  41. package/lib-esm/model/hips/HiPSCoverage.js.map +1 -1
  42. package/lib-esm/model/hips/Tile.d.ts +0 -1
  43. package/lib-esm/model/hips/Tile.d.ts.map +1 -1
  44. package/lib-esm/model/hips/Tile.js +9 -27
  45. package/lib-esm/model/hips/Tile.js.map +1 -1
  46. package/lib-esm/model/hips/TileBuffer.d.ts.map +1 -1
  47. package/lib-esm/model/hips/TileBuffer.js +6 -0
  48. package/lib-esm/model/hips/TileBuffer.js.map +1 -1
  49. package/lib-esm/shader/HiPSShaderProgram.d.ts +7 -1
  50. package/lib-esm/shader/HiPSShaderProgram.d.ts.map +1 -1
  51. package/lib-esm/shader/HiPSShaderProgram.js +23 -1
  52. package/lib-esm/shader/HiPSShaderProgram.js.map +1 -1
  53. package/lib-esm/shader/ShaderManager.d.ts.map +1 -1
  54. package/lib-esm/shader/ShaderManager.js +46 -10
  55. package/lib-esm/shader/ShaderManager.js.map +1 -1
  56. package/package.json +1 -1
@@ -3638,6 +3638,7 @@ class AstroSphere {
3638
3638
  pointerDownY = null;
3639
3639
  pointerDownAt = 0;
3640
3640
  _activeHiPS = null;
3641
+ _activeHiPSLayers = [];
3641
3642
  _activeXYZ2 = null;
3642
3643
  _activeMeshHiPS = null;
3643
3644
  _activeBaseLayer = null;
@@ -4140,12 +4141,84 @@ class AstroSphere {
4140
4141
  }
4141
4142
  this._activeHiPS.changeFormat(format);
4142
4143
  }
4144
+ setHiPSFITSScaleFunction(scaleFunction, scaleParam) {
4145
+ if (!this._activeHiPS) {
4146
+ throw new Error("No active HiPS.");
4147
+ }
4148
+ this._activeHiPS.setFITSScaleFunction(scaleFunction, scaleParam);
4149
+ }
4150
+ getActiveHiPSFITSStretch() {
4151
+ return this._activeHiPS?.fitsStretch ?? null;
4152
+ }
4153
+ setHiPSFITSRangeMode(rangeMode) {
4154
+ if (!this._activeHiPS) {
4155
+ throw new Error("No active HiPS.");
4156
+ }
4157
+ this._activeHiPS.setFITSRangeMode(rangeMode);
4158
+ }
4159
+ createHiPS(hipsDescriptor) {
4160
+ return new HiPS_js_1.HiPS(1, [0.0, 0.0, 0.0], 0, 0, hipsDescriptor, this._webgl, this._healpixGrid);
4161
+ }
4143
4162
  activateHiPS(hipsDescriptor) {
4144
- if (this._activeHiPS) {
4145
- this._healpixGrid.visibleTilesManager.tileBuffer.removeHiPS(this._activeHiPS);
4163
+ const tileBuffer = this._healpixGrid.visibleTilesManager.tileBuffer;
4164
+ for (const hips of this._activeHiPSLayers) {
4165
+ tileBuffer.removeHiPS(hips);
4166
+ }
4167
+ this._activeHiPSLayers = [];
4168
+ const hips = this.createHiPS(hipsDescriptor);
4169
+ this._activeHiPSLayers.push(hips);
4170
+ this._activeHiPS = hips;
4171
+ this._activeBaseLayer = "hips";
4172
+ return hips;
4173
+ }
4174
+ setHiPSOpacity(hips, opacity) {
4175
+ if (!this._activeHiPSLayers.includes(hips)) {
4176
+ throw new Error("HiPS layer is not active in this AstroSphere.");
4177
+ }
4178
+ hips.setOpacity(opacity);
4179
+ }
4180
+ addHiPS(hipsDescriptor) {
4181
+ const normalizeURL = (url) => String(url).replace(/\/+$/, "");
4182
+ const descriptorURL = normalizeURL(hipsDescriptor.url);
4183
+ const existing = this._activeHiPSLayers.find((hips) => normalizeURL(hips.baseURL) === descriptorURL);
4184
+ if (existing) {
4185
+ throw new Error(`HiPS already active: ${String(hipsDescriptor.url)}`);
4146
4186
  }
4147
- this._activeHiPS = new HiPS_js_1.HiPS(1, [0.0, 0.0, 0.0], 0, 0, hipsDescriptor, this._webgl, this._healpixGrid);
4187
+ const hips = this.createHiPS(hipsDescriptor);
4188
+ this._activeHiPSLayers.push(hips);
4189
+ this._activeHiPS = hips;
4148
4190
  this._activeBaseLayer = "hips";
4191
+ return hips;
4192
+ }
4193
+ removeHiPS(hips) {
4194
+ const index = this._activeHiPSLayers.indexOf(hips);
4195
+ if (index === -1) {
4196
+ return;
4197
+ }
4198
+ this._healpixGrid.visibleTilesManager.tileBuffer.removeHiPS(hips);
4199
+ this._activeHiPSLayers.splice(index, 1);
4200
+ if (this._activeHiPSLayers.length === 0) {
4201
+ this._activeHiPS = null;
4202
+ if (this._activeBaseLayer === "hips") {
4203
+ this._activeBaseLayer = null;
4204
+ }
4205
+ return;
4206
+ }
4207
+ if (this._activeHiPS === hips) {
4208
+ this._activeHiPS =
4209
+ this._activeHiPSLayers[this._activeHiPSLayers.length - 1];
4210
+ }
4211
+ }
4212
+ removeAllHiPS() {
4213
+ const tileBuffer = this._healpixGrid.visibleTilesManager.tileBuffer;
4214
+ for (const hips of this._activeHiPSLayers) {
4215
+ tileBuffer.removeHiPS(hips);
4216
+ }
4217
+ this._activeHiPSLayers = [];
4218
+ this._activeHiPS = null;
4219
+ if (this._activeBaseLayer === "hips") {
4220
+ this._activeBaseLayer = null;
4221
+ }
4149
4222
  }
4150
4223
  activateXYZ(config) {
4151
4224
  this.activateXYZ2(new XYZMapDescriptor_js_1.XYZMapDescriptor(config.name ?? "XYZ Earth2 Layer", config.urlTemplate, config.minZoom ?? 0, config.maxZoom ?? 8, config.segmentsPerSide ?? 48, config.maxCachedTiles ?? 384, 8, config.urlResolver));
@@ -4378,6 +4451,16 @@ class AstroSphere {
4378
4451
  get activeHiPS() {
4379
4452
  return this._activeHiPS;
4380
4453
  }
4454
+ get activeHiPSLayers() {
4455
+ return this._activeHiPSLayers;
4456
+ }
4457
+ setActiveHiPS(hips) {
4458
+ if (!this._activeHiPSLayers.includes(hips)) {
4459
+ throw new Error("HiPS layer is not active in this AstroSphere.");
4460
+ }
4461
+ this._activeHiPS = hips;
4462
+ this._activeBaseLayer = "hips";
4463
+ }
4381
4464
  get activeXYZ() {
4382
4465
  return this._activeXYZ2;
4383
4466
  }
@@ -4537,8 +4620,9 @@ class AstroSphere {
4537
4620
  this._webgl.enable(this._webgl.CULL_FACE);
4538
4621
  this._webgl.cullFace(Global_js_1.default.insideSphere ? this._webgl.FRONT : this._webgl.BACK);
4539
4622
  this._webgl.blendFunc(this._webgl.SRC_ALPHA, this._webgl.ONE_MINUS_SRC_ALPHA);
4540
- if (this._activeBaseLayer === "hips" && this._activeHiPS) {
4541
- const visibleOrder = Math.min(this._healpixGrid.visibleorder, this._activeHiPS.maxOrder);
4623
+ if (this._activeBaseLayer === "hips" && this._activeHiPSLayers.length > 0) {
4624
+ const maxHiPSOrder = Math.max(...this._activeHiPSLayers.map((hips) => hips.maxOrder));
4625
+ const visibleOrder = Math.min(this._healpixGrid.visibleorder, maxHiPSOrder);
4542
4626
  this._healpixGrid.visibleTilesManager.computeVisiblePixels(visibleOrder, this._webgl, this._camera, this._perspectiveMatrixManager.pMatrix);
4543
4627
  }
4544
4628
  if (this._activeBaseLayer === "meships" && this._activeMeshHiPS) {
@@ -4563,6 +4647,11 @@ class AstroSphere {
4563
4647
  cameraMoving: cameraMovingForGrid,
4564
4648
  };
4565
4649
  if (this._activeBaseLayer === "hips") {
4650
+ for (const hips of this._activeHiPSLayers) {
4651
+ if (hips !== this._activeHiPS) {
4652
+ hips.draw(skyEntityDrawInput);
4653
+ }
4654
+ }
4566
4655
  this._activeHiPS?.draw(skyEntityDrawInput);
4567
4656
  }
4568
4657
  if (this._activeBaseLayer === "xyz") {
@@ -4898,21 +4987,56 @@ class AstroViewer {
4898
4987
  this.astroSphere.activateMeshHiPS(desc);
4899
4988
  return desc.name;
4900
4989
  }
4901
- // changeColorMap(hips: HiPS, colorMapName: ColorMapName) {
4990
+ setActiveHiPS(hips) {
4991
+ this.astroSphere.setActiveHiPS(hips);
4992
+ }
4993
+ setHiPSOpacity(hips, opacity) {
4994
+ this.astroSphere.setHiPSOpacity(hips, opacity);
4995
+ }
4902
4996
  changeColorMap(colorMapName) {
4903
4997
  const colorMap = ColorMaps_js_1.default[colorMapName];
4904
- // hips.changeColorMap(colorMap)
4905
4998
  this.astroSphere.changeColorMap(colorMap);
4906
4999
  }
4907
5000
  changeCustomColorMap(colorMap) {
4908
5001
  this.astroSphere.changeColorMap(colorMap);
4909
5002
  }
5003
+ addHiPS(hipsDescriptor) {
5004
+ return this.astroSphere.addHiPS(hipsDescriptor);
5005
+ }
5006
+ async addHiPSFromUrl(baseUrl) {
5007
+ const hipsUrl = baseUrl.endsWith("/") ? baseUrl : baseUrl + "/";
5008
+ const resp = await fetch(hipsUrl + "properties");
5009
+ if (!resp.ok) {
5010
+ throw new Error(`HTTP ${resp.status} fetching properties`);
5011
+ }
5012
+ const propsText = await resp.text();
5013
+ const desc = new HiPSDescriptor_js_1.HiPSDescriptor(propsText, hipsUrl);
5014
+ return this.astroSphere.addHiPS(desc);
5015
+ }
5016
+ removeHiPS(hips) {
5017
+ this.astroSphere.removeHiPS(hips);
5018
+ }
5019
+ removeAllHiPS() {
5020
+ this.astroSphere.removeAllHiPS();
5021
+ }
5022
+ getActiveHiPSLayers() {
5023
+ return this.astroSphere.activeHiPSLayers;
5024
+ }
4910
5025
  getActiveHiPS() {
4911
5026
  return this.astroSphere.activeHiPS;
4912
5027
  }
4913
5028
  changeHiPSFormat(format) {
4914
5029
  this.astroSphere.changeHiPSFormat(format);
4915
5030
  }
5031
+ setHiPSFITSScaleFunction(scaleFunction, scaleParam) {
5032
+ this.astroSphere.setHiPSFITSScaleFunction(scaleFunction, scaleParam);
5033
+ }
5034
+ getActiveHiPSFITSStretch() {
5035
+ return this.astroSphere.getActiveHiPSFITSStretch();
5036
+ }
5037
+ setHiPSFITSRangeMode(rangeMode) {
5038
+ this.astroSphere.setHiPSFITSRangeMode(rangeMode);
5039
+ }
4916
5040
  getActiveHiPSFormats() {
4917
5041
  return this.astroSphere.activeHiPS?.availableFormats ?? [];
4918
5042
  }
@@ -5783,7 +5907,6 @@ var Source_js_1 = __webpack_require__(146);
5783
5907
  Object.defineProperty(exports, "Source", ({ enumerable: true, get: function () { return Source_js_1.Source; } }));
5784
5908
  var Footprint_js_1 = __webpack_require__(2475);
5785
5909
  Object.defineProperty(exports, "Footprint", ({ enumerable: true, get: function () { return Footprint_js_1.Footprint; } }));
5786
- console.log('astroviewer UMD loaded');
5787
5910
 
5788
5911
 
5789
5912
  /***/ },
@@ -11573,8 +11696,14 @@ class HealpixGrid extends AbstractSkyEntity_js_1.AbstractSkyEntity {
11573
11696
  // FIX: pass model & pMatrix to match FoVUtils TS signature
11574
11697
  const center = FoVUtils_js_1.FoVUtils.getCenterJ2000(gl.canvas, this, this._webgl, camera, pMatrix);
11575
11698
  const fovMin = (this.getMinFoV() * Math.PI) / 180 / 2;
11699
+ const visibleHealpix = Global_js_1.default.getHealpix(this._visibleorder);
11700
+ const maxPixel = visibleHealpix.getNPix();
11576
11701
  for (let p = 0; p < pixels.length; p++) {
11577
- const pixCenter = Global_js_1.default.getHealpix(this._visibleorder).pix2vec(pixels[p]);
11702
+ const pixel = pixels[p];
11703
+ if (!Number.isInteger(pixel) || pixel < 0 || pixel >= maxPixel) {
11704
+ continue;
11705
+ }
11706
+ const pixCenter = visibleHealpix.pix2vec(pixel);
11578
11707
  // const pixCenter = (global.getHealpix(global.order).pix2vec(pixels[p]) as BoundVec);
11579
11708
  const point = new Point_js_1.Point({ x: pixCenter.x, y: pixCenter.y, z: pixCenter.z }, CoordsType_js_1.CoordsType.CARTESIAN);
11580
11709
  const distance = GeomUtils_js_1.default.orthodromicDistance(center, point);
@@ -11957,7 +12086,7 @@ class AllSky {
11957
12086
  _baseurl;
11958
12087
  _isGalacticHips;
11959
12088
  _order = 3;
11960
- opacity = 1.0;
12089
+ // public opacity = 1.0;
11961
12090
  _hipsShaderIndex = 0;
11962
12091
  _texture = null;
11963
12092
  _image;
@@ -12004,6 +12133,7 @@ class AllSky {
12004
12133
  async loadFits() {
12005
12134
  try {
12006
12135
  this._fitsTile = await (0, FitsTileLoader_js_1.loadFitsTile)(this._texurl);
12136
+ this._hips.observeFITSDataRange(this._fitsTile);
12007
12137
  this.textureLoaded();
12008
12138
  this.initModelBuffer();
12009
12139
  this._textureLoaded = true;
@@ -12048,30 +12178,7 @@ class AllSky {
12048
12178
  }
12049
12179
  }
12050
12180
  getFitsDataRange() {
12051
- const hipsRange = this._hips.dataRange;
12052
- if (hipsRange.min !== undefined &&
12053
- hipsRange.max !== undefined &&
12054
- Number.isFinite(hipsRange.min) &&
12055
- Number.isFinite(hipsRange.max) &&
12056
- hipsRange.max > hipsRange.min) {
12057
- return {
12058
- min: hipsRange.min,
12059
- max: hipsRange.max,
12060
- };
12061
- }
12062
- const fits = this._fitsTile;
12063
- if (fits &&
12064
- fits.dataMin !== null &&
12065
- fits.dataMax !== null &&
12066
- Number.isFinite(fits.dataMin) &&
12067
- Number.isFinite(fits.dataMax) &&
12068
- fits.dataMax > fits.dataMin) {
12069
- return {
12070
- min: fits.dataMin,
12071
- max: fits.dataMax,
12072
- };
12073
- }
12074
- return null;
12181
+ return this._hips.getFITSDisplayRange(this._fitsTile);
12075
12182
  }
12076
12183
  initModelBuffer() {
12077
12184
  const gl = this._webgl;
@@ -12185,7 +12292,7 @@ class AllSky {
12185
12292
  console.error(`[AllSky] No display range available for FITS atlas ${this._texurl}`);
12186
12293
  return false;
12187
12294
  }
12188
- this._hipsShaderProgram.setTextureDataMode(this._hipsShaderIndex, true, range.min, range.max);
12295
+ this._hipsShaderProgram.setTextureDataMode(this._hipsShaderIndex, true, range.min, range.max, this._hips.fitsStretch.scaleFunctionIndex, this._hips.fitsStretch.scaleParam);
12189
12296
  }
12190
12297
  else {
12191
12298
  this._hipsShaderProgram.setTextureDataMode(this._hipsShaderIndex, false);
@@ -12198,9 +12305,10 @@ class AllSky {
12198
12305
  gl.activeTexture(gl.TEXTURE0 + this._hipsShaderIndex);
12199
12306
  gl.bindTexture(gl.TEXTURE_2D, this._texture);
12200
12307
  const factorLocation = this._hipsShaderProgram.locations.textureAlpha[this._hipsShaderIndex];
12201
- if (factorLocation) {
12202
- gl.uniform1f(factorLocation, this.opacity);
12308
+ if (factorLocation !== null) {
12309
+ gl.uniform1f(factorLocation, 1.0);
12203
12310
  }
12311
+ this._hipsShaderProgram.setLayerOpacity(this._hips.opacity);
12204
12312
  gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexPositionBuffer);
12205
12313
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.vertexIndexBuffer);
12206
12314
  gl.vertexAttribPointer(
@@ -12228,13 +12336,13 @@ class AllSky {
12228
12336
  const allSkyTiles2Skip = [];
12229
12337
  for (let i = 0; i < visibleTiles.length; i++) {
12230
12338
  const tileno = visibleTiles[i];
12339
+ if (!this._hips.intersectsCoverage(childrenOrder, tileno)) {
12340
+ allSkyTiles2Skip.push(tileno);
12341
+ continue;
12342
+ }
12231
12343
  const childTile = this._isGalacticHips
12232
12344
  ? this._tileBuffer.getGalTile(tileno, childrenOrder, this._hips)
12233
12345
  : this._tileBuffer.getTile(tileno, childrenOrder, this._hips);
12234
- // const childTile = this._isGalacticHips
12235
- // ? newTileBuffer.getGalTile(tileno, childrenOrder, this._hips)
12236
- // : newTileBuffer.getTile(tileno, childrenOrder, this._hips)
12237
- // childTile.draw(visibleOrder, visibleTilesMap, pMatrix, vMatrix, mMatrix, colorMapIdx)
12238
12346
  childTile.draw(visibleOrder, visibleTilesMap, pMatrix, vMatrix, mMatrix, colorMapIdx, this._hipsShaderProgram);
12239
12347
  if (childTile.getReadyState()) {
12240
12348
  allSkyTiles2Skip.push(tileno);
@@ -12322,6 +12430,7 @@ class AncestorTile {
12322
12430
  async loadFits() {
12323
12431
  try {
12324
12432
  this._fitsTile = await (0, FitsTileLoader_js_1.loadFitsTile)(this._texurl);
12433
+ this._hips.observeFITSDataRange(this._fitsTile);
12325
12434
  this.textureLoaded();
12326
12435
  const fits = this._fitsTile;
12327
12436
  if (!fits) {
@@ -12379,30 +12488,7 @@ class AncestorTile {
12379
12488
  }
12380
12489
  }
12381
12490
  getFitsDataRange() {
12382
- const hipsRange = this._hips.dataRange;
12383
- if (hipsRange.min !== undefined &&
12384
- hipsRange.max !== undefined &&
12385
- Number.isFinite(hipsRange.min) &&
12386
- Number.isFinite(hipsRange.max) &&
12387
- hipsRange.max > hipsRange.min) {
12388
- return {
12389
- min: hipsRange.min,
12390
- max: hipsRange.max,
12391
- };
12392
- }
12393
- const fits = this._fitsTile;
12394
- if (fits &&
12395
- fits.dataMin !== null &&
12396
- fits.dataMax !== null &&
12397
- Number.isFinite(fits.dataMin) &&
12398
- Number.isFinite(fits.dataMax) &&
12399
- fits.dataMax > fits.dataMin) {
12400
- return {
12401
- min: fits.dataMin,
12402
- max: fits.dataMax,
12403
- };
12404
- }
12405
- return null;
12491
+ return this._hips.getFITSDisplayRange(this._fitsTile);
12406
12492
  }
12407
12493
  initModelBuffer() {
12408
12494
  const gl = this._webgl;
@@ -12502,7 +12588,7 @@ class AncestorTile {
12502
12588
  console.error(`[AncestorTile] No display range available for FITS tile ${this._texurl}`);
12503
12589
  return false;
12504
12590
  }
12505
- this._hipsShaderProgram.setTextureDataMode(this._hipsShaderIndex, true, range.min, range.max);
12591
+ this._hipsShaderProgram.setTextureDataMode(this._hipsShaderIndex, true, range.min, range.max, this._hips.fitsStretch.scaleFunctionIndex, this._hips.fitsStretch.scaleParam);
12506
12592
  }
12507
12593
  else {
12508
12594
  this._hipsShaderProgram.setTextureDataMode(this._hipsShaderIndex, false);
@@ -12535,16 +12621,21 @@ class AncestorTile {
12535
12621
  return;
12536
12622
  for (let c = 0; c < 4; c++) {
12537
12623
  const childTileNo = (this._tileno << 2) + c;
12624
+ if (!this._hips.intersectsCoverage(childrenOrder, childTileNo)) {
12625
+ continue;
12626
+ }
12538
12627
  const visibleChildren = visibleTilesMap.get(childrenOrder);
12539
12628
  if (visibleChildren.includes(childTileNo)) {
12629
+ if (!this._hips.intersectsCoverage(childrenOrder, childTileNo)) {
12630
+ quadrantsToDraw.delete(c);
12631
+ continue;
12632
+ }
12540
12633
  const childTile = this._isGalacticHips
12541
12634
  ? this._tileBuffer.getGalTile(childTileNo, childrenOrder, this._hips)
12542
12635
  : this._tileBuffer.getTile(childTileNo, childrenOrder, this._hips);
12543
- // childTile.draw(visibleOrder, visibleTilesMap, pMatrix, vMatrix, mMatrix, colorMapIdx)
12544
12636
  childTile.draw(visibleOrder, visibleTilesMap, pMatrix, vMatrix, mMatrix, colorMapIdx, this._hipsShaderProgram);
12545
12637
  if (childTile.getReadyState()) {
12546
12638
  quadrantsToDraw.delete(c);
12547
- // quadrantsToDraw.delete((childTile as any)._tileno - (this._tileno << 2))
12548
12639
  }
12549
12640
  }
12550
12641
  }
@@ -12575,6 +12666,9 @@ var __webpack_unused_export__;
12575
12666
  __webpack_unused_export__ = ({ value: true });
12576
12667
  exports.loadFitsTile = loadFitsTile;
12577
12668
  const jsfitsio_1 = __webpack_require__(1677);
12669
+ const ROBUST_SAMPLE_COUNT = 8192;
12670
+ const ROBUST_LOW_PERCENTILE = 0.005;
12671
+ const ROBUST_HIGH_PERCENTILE = 0.995;
12578
12672
  async function loadFitsTile(url) {
12579
12673
  const fitsFile = await jsfitsio_1.FITSParser.loadFITSFile(url);
12580
12674
  if (!fitsFile?.primaryHDU) {
@@ -12604,22 +12698,57 @@ async function loadFitsTile(url) {
12604
12698
  const dataMin = getNumericHeaderValue(image.header, jsfitsio_1.FITSHeaderManager.DATAMIN);
12605
12699
  const dataMax = getNumericHeaderValue(image.header, jsfitsio_1.FITSHeaderManager.DATAMAX);
12606
12700
  const pixels = new Float32Array(data.length);
12701
+ let physicalMin = null;
12702
+ let physicalMax = null;
12703
+ const robustSamples = [];
12704
+ const sampleStep = Math.max(1, Math.floor(data.length / ROBUST_SAMPLE_COUNT));
12607
12705
  for (let i = 0; i < data.length; i++) {
12608
12706
  const rawValue = data[i];
12609
12707
  if (!Number.isFinite(rawValue) || (blank !== null && rawValue === blank)) {
12610
12708
  pixels[i] = Number.NaN;
12611
12709
  continue;
12612
12710
  }
12613
- pixels[i] = rawValue * bscale + bzero;
12711
+ const physicalValue = rawValue * bscale + bzero;
12712
+ pixels[i] = physicalValue;
12713
+ if (Number.isFinite(physicalValue)) {
12714
+ physicalMin =
12715
+ physicalMin === null ? physicalValue : Math.min(physicalMin, physicalValue);
12716
+ physicalMax =
12717
+ physicalMax === null ? physicalValue : Math.max(physicalMax, physicalValue);
12718
+ if (i % sampleStep === 0) {
12719
+ robustSamples.push(physicalValue);
12720
+ }
12721
+ }
12614
12722
  }
12723
+ const robustRange = computeRobustRange(robustSamples);
12615
12724
  return {
12616
12725
  width,
12617
12726
  height,
12618
12727
  pixels,
12619
12728
  dataMin,
12620
12729
  dataMax,
12730
+ physicalMin,
12731
+ physicalMax,
12732
+ robustMin: robustRange?.min ?? null,
12733
+ robustMax: robustRange?.max ?? null,
12621
12734
  };
12622
12735
  }
12736
+ function computeRobustRange(samples) {
12737
+ if (samples.length === 0) {
12738
+ return null;
12739
+ }
12740
+ samples.sort((a, b) => a - b);
12741
+ const min = percentile(samples, ROBUST_LOW_PERCENTILE);
12742
+ const max = percentile(samples, ROBUST_HIGH_PERCENTILE);
12743
+ if (!Number.isFinite(min) || !Number.isFinite(max) || max <= min) {
12744
+ return null;
12745
+ }
12746
+ return { min, max };
12747
+ }
12748
+ function percentile(sortedValues, p) {
12749
+ const index = Math.min(sortedValues.length - 1, Math.max(0, Math.round((sortedValues.length - 1) * p)));
12750
+ return sortedValues[index];
12751
+ }
12623
12752
  function getNumericHeaderValue(header, key) {
12624
12753
  const item = header.findById(key);
12625
12754
  if (!item) {
@@ -12824,10 +12953,29 @@ const FoVHelper_js_1 = __webpack_require__(229);
12824
12953
  const ColorMaps_js_1 = __importDefault(__webpack_require__(619));
12825
12954
  const AncestorTile_js_1 = __importDefault(__webpack_require__(2885));
12826
12955
  const AllSky_js_1 = __importDefault(__webpack_require__(2368));
12956
+ const HiPSCoverage_js_1 = __webpack_require__(9912);
12957
+ const FITS_SCALE_FUNCTION_INDEX = {
12958
+ linear: 0,
12959
+ sqrt: 1,
12960
+ log: 2,
12961
+ asinh: 3,
12962
+ gamma: 4,
12963
+ };
12964
+ const DEFAULT_FITS_SCALE_PARAM = {
12965
+ linear: 1,
12966
+ sqrt: 1,
12967
+ log: 100,
12968
+ asinh: 10,
12969
+ gamma: 0.5,
12970
+ };
12971
+ const MAX_ROBUST_TILE_RANGES = 512;
12827
12972
  class HiPS extends AbstractSkyEntity_js_1.AbstractSkyEntity {
12828
12973
  _ancestorTiles;
12829
12974
  _allSkyTile;
12830
12975
  _descriptor;
12976
+ _coverage;
12977
+ _coverageResolved = false;
12978
+ _opacity = 1.0;
12831
12979
  _format;
12832
12980
  _baseurl;
12833
12981
  _maxorder;
@@ -12837,6 +12985,13 @@ class HiPS extends AbstractSkyEntity_js_1.AbstractSkyEntity {
12837
12985
  samplerIdx = 0;
12838
12986
  colorMapIdx = 0;
12839
12987
  colorMap = ColorMaps_js_1.default["native"];
12988
+ _fitsStretch = {
12989
+ scaleFunction: "linear",
12990
+ scaleFunctionIndex: FITS_SCALE_FUNCTION_INDEX.linear,
12991
+ scaleParam: DEFAULT_FITS_SCALE_PARAM.linear,
12992
+ rangeMode: "robust",
12993
+ };
12994
+ _robustFITSRanges = [];
12840
12995
  _healpixGrid;
12841
12996
  // exposed read-only helpers
12842
12997
  get maxOrder() {
@@ -12866,6 +13021,17 @@ class HiPS extends AbstractSkyEntity_js_1.AbstractSkyEntity {
12866
13021
  this._baseurl = descriptor.url;
12867
13022
  this._maxorder = descriptor.maxOrder;
12868
13023
  this._minorder = descriptor.minOrder;
13024
+ this._coverage = new HiPSCoverage_js_1.HiPSCoverage();
13025
+ void this._coverage
13026
+ .load(this._baseurl)
13027
+ .then(() => {
13028
+ this._coverageResolved = true;
13029
+ })
13030
+ .catch((error) => {
13031
+ console.warn(`[HiPS] Coverage unavailable for ${this._baseurl}; continuing without MOC filtering.`, error);
13032
+ this._coverage.clear();
13033
+ this._coverageResolved = true;
13034
+ });
12869
13035
  if (this.isGalacticHips) {
12870
13036
  this._healpixGrid.visibleTilesManager.tileBuffer.addGalHiPS(this);
12871
13037
  }
@@ -12888,6 +13054,31 @@ class HiPS extends AbstractSkyEntity_js_1.AbstractSkyEntity {
12888
13054
  this._allSky = true;
12889
13055
  this.initBaseTiles();
12890
13056
  }
13057
+ setOpacity(opacity) {
13058
+ if (!Number.isFinite(opacity)) {
13059
+ throw new Error(`Invalid HiPS opacity: ${opacity}`);
13060
+ }
13061
+ this._opacity = Math.min(1, Math.max(0, opacity));
13062
+ }
13063
+ get opacity() {
13064
+ return this._opacity;
13065
+ }
13066
+ intersectsCoverage(order, pixel) {
13067
+ /*
13068
+ * While Moc.fits is being resolved, do not start tile requests.
13069
+ *
13070
+ * Once loading has completed:
13071
+ * - valid MOC -> filter using coverage
13072
+ * - unavailable MOC -> HiPSCoverage is cleared and normal loading resumes
13073
+ */
13074
+ if (!this._coverageResolved) {
13075
+ return false;
13076
+ }
13077
+ return this._coverage.intersectsTile(order, pixel);
13078
+ }
13079
+ get coverageLoaded() {
13080
+ return this._coverage.loaded;
13081
+ }
12891
13082
  initBaseTiles() {
12892
13083
  this._ancestorTiles = [];
12893
13084
  this._allSkyTile = null;
@@ -13036,6 +13227,137 @@ class HiPS extends AbstractSkyEntity_js_1.AbstractSkyEntity {
13036
13227
  get dataRange() {
13037
13228
  return this._descriptor.dataRange;
13038
13229
  }
13230
+ get fitsStretch() {
13231
+ return this._fitsStretch;
13232
+ }
13233
+ setFITSScaleFunction(scaleFunction, scaleParam) {
13234
+ const scaleFunctionIndex = FITS_SCALE_FUNCTION_INDEX[scaleFunction];
13235
+ if (scaleFunctionIndex === undefined) {
13236
+ throw new Error(`Unsupported FITS scale function: ${scaleFunction}`);
13237
+ }
13238
+ const defaultParam = DEFAULT_FITS_SCALE_PARAM[scaleFunction];
13239
+ const param = scaleParam ?? defaultParam;
13240
+ if (!Number.isFinite(param) || param <= 0) {
13241
+ throw new Error(`Invalid FITS scale parameter: ${param}`);
13242
+ }
13243
+ this._fitsStretch = {
13244
+ ...this._fitsStretch,
13245
+ scaleFunction,
13246
+ scaleFunctionIndex,
13247
+ scaleParam: param,
13248
+ };
13249
+ }
13250
+ setFITSRangeMode(rangeMode) {
13251
+ if (rangeMode !== "robust" && rangeMode !== "hips" && rangeMode !== "tile") {
13252
+ throw new Error(`Unsupported FITS range mode: ${rangeMode}`);
13253
+ }
13254
+ this._fitsStretch = {
13255
+ ...this._fitsStretch,
13256
+ rangeMode,
13257
+ };
13258
+ }
13259
+ observeFITSDataRange(fits) {
13260
+ if (fits.robustMin === null ||
13261
+ fits.robustMax === null ||
13262
+ !Number.isFinite(fits.robustMin) ||
13263
+ !Number.isFinite(fits.robustMax) ||
13264
+ fits.robustMax <= fits.robustMin) {
13265
+ return;
13266
+ }
13267
+ this._robustFITSRanges.push({
13268
+ min: fits.robustMin,
13269
+ max: fits.robustMax,
13270
+ });
13271
+ if (this._robustFITSRanges.length > MAX_ROBUST_TILE_RANGES) {
13272
+ this._robustFITSRanges.shift();
13273
+ }
13274
+ }
13275
+ getFITSDisplayRange(fits) {
13276
+ const hipsRange = this.getHiPSDataRange();
13277
+ if (this._fitsStretch.rangeMode === "hips" && hipsRange) {
13278
+ return hipsRange;
13279
+ }
13280
+ if (this._fitsStretch.rangeMode === "robust") {
13281
+ const robustRange = this.getRobustFITSRange() ?? this.getTileRobustRange(fits);
13282
+ if (robustRange) {
13283
+ return robustRange;
13284
+ }
13285
+ }
13286
+ const tileRange = this.getTilePhysicalRange(fits) ?? this.getTileHeaderRange(fits);
13287
+ if (tileRange) {
13288
+ return tileRange;
13289
+ }
13290
+ return hipsRange;
13291
+ }
13292
+ getHiPSDataRange() {
13293
+ const hipsRange = this.dataRange;
13294
+ if (hipsRange.min !== undefined &&
13295
+ hipsRange.max !== undefined &&
13296
+ Number.isFinite(hipsRange.min) &&
13297
+ Number.isFinite(hipsRange.max) &&
13298
+ hipsRange.max > hipsRange.min) {
13299
+ return {
13300
+ min: hipsRange.min,
13301
+ max: hipsRange.max,
13302
+ };
13303
+ }
13304
+ return null;
13305
+ }
13306
+ getRobustFITSRange() {
13307
+ if (this._robustFITSRanges.length === 0) {
13308
+ return null;
13309
+ }
13310
+ const lows = this._robustFITSRanges.map((range) => range.min).sort((a, b) => a - b);
13311
+ const highs = this._robustFITSRanges.map((range) => range.max).sort((a, b) => a - b);
13312
+ const min = lows[Math.floor((lows.length - 1) * 0.1)];
13313
+ const max = highs[Math.ceil((highs.length - 1) * 0.9)];
13314
+ if (!Number.isFinite(min) || !Number.isFinite(max) || max <= min) {
13315
+ return null;
13316
+ }
13317
+ return { min, max };
13318
+ }
13319
+ getTileRobustRange(fits) {
13320
+ if (fits &&
13321
+ fits.robustMin !== null &&
13322
+ fits.robustMax !== null &&
13323
+ Number.isFinite(fits.robustMin) &&
13324
+ Number.isFinite(fits.robustMax) &&
13325
+ fits.robustMax > fits.robustMin) {
13326
+ return {
13327
+ min: fits.robustMin,
13328
+ max: fits.robustMax,
13329
+ };
13330
+ }
13331
+ return null;
13332
+ }
13333
+ getTilePhysicalRange(fits) {
13334
+ if (fits &&
13335
+ fits.physicalMin !== null &&
13336
+ fits.physicalMax !== null &&
13337
+ Number.isFinite(fits.physicalMin) &&
13338
+ Number.isFinite(fits.physicalMax) &&
13339
+ fits.physicalMax > fits.physicalMin) {
13340
+ return {
13341
+ min: fits.physicalMin,
13342
+ max: fits.physicalMax,
13343
+ };
13344
+ }
13345
+ return null;
13346
+ }
13347
+ getTileHeaderRange(fits) {
13348
+ if (fits &&
13349
+ fits.dataMin !== null &&
13350
+ fits.dataMax !== null &&
13351
+ Number.isFinite(fits.dataMin) &&
13352
+ Number.isFinite(fits.dataMax) &&
13353
+ fits.dataMax > fits.dataMin) {
13354
+ return {
13355
+ min: fits.dataMin,
13356
+ max: fits.dataMax,
13357
+ };
13358
+ }
13359
+ return null;
13360
+ }
13039
13361
  getCurrentHealpixOrder() {
13040
13362
  return this._visibleorder;
13041
13363
  }
@@ -13099,6 +13421,184 @@ class HiPS extends AbstractSkyEntity_js_1.AbstractSkyEntity {
13099
13421
  exports.HiPS = HiPS;
13100
13422
 
13101
13423
 
13424
+ /***/ },
13425
+
13426
+ /***/ 9912
13427
+ (__unused_webpack_module, exports, __webpack_require__) {
13428
+
13429
+ "use strict";
13430
+ var __webpack_unused_export__;
13431
+
13432
+ /*
13433
+ * AstroViewer
13434
+ * Copyright (C) Fabrizio Giordano
13435
+ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-AstroViewer-Commercial
13436
+ *
13437
+ * AstroViewer is dual-licensed under the GNU Affero General Public License
13438
+ * version 3 and a separate commercial license.
13439
+ *
13440
+ * See LICENSE.md, LICENSE-AGPL.md, and LICENSE-COMMERCIAL.md for details.
13441
+ */
13442
+ __webpack_unused_export__ = ({ value: true });
13443
+ exports.HiPSCoverage = void 0;
13444
+ const jsfitsio_1 = __webpack_require__(1677);
13445
+ class HiPSCoverage {
13446
+ intersec(intersec, arg1) {
13447
+ throw new Error("Method not implemented.");
13448
+ }
13449
+ ises(ises, expents, of, arg3) {
13450
+ throw new Error("Method not implemented.");
13451
+ }
13452
+ intersexpect(co, t, expe, arg3) {
13453
+ throw new Error("Method not implemented.");
13454
+ }
13455
+ intersectsT19(intersectsT19, arg1) {
13456
+ throw new Error("Method not implemented.");
13457
+ }
13458
+ _maxOrder = 0;
13459
+ _intervals = [];
13460
+ _loaded = false;
13461
+ get loaded() {
13462
+ return this._loaded;
13463
+ }
13464
+ get maxOrder() {
13465
+ return this._maxOrder;
13466
+ }
13467
+ get intervalCount() {
13468
+ return this._intervals.length;
13469
+ }
13470
+ clear() {
13471
+ this._maxOrder = 0;
13472
+ this._intervals = [];
13473
+ this._loaded = false;
13474
+ }
13475
+ async load(baseUrl) {
13476
+ const baseUrlString = baseUrl instanceof URL ? baseUrl.href : String(baseUrl);
13477
+ const normalizedBaseUrl = baseUrlString.endsWith("/")
13478
+ ? baseUrlString
13479
+ : `${baseUrlString}/`;
13480
+ await this.loadMoc(`${normalizedBaseUrl}Moc.fits`);
13481
+ }
13482
+ async loadMoc(mocUrl) {
13483
+ this.clear();
13484
+ const fits = await jsfitsio_1.FITSParser.loadFITSFile(mocUrl);
13485
+ if (!fits) {
13486
+ throw new Error("Unable to load HiPS Moc.fits");
13487
+ }
13488
+ const mocHDU = fits.hdus.find((hdu) => hdu instanceof jsfitsio_1.BinaryTableHDU);
13489
+ if (!(mocHDU instanceof jsfitsio_1.BinaryTableHDU)) {
13490
+ throw new Error("HiPS Moc.fits does not contain a BINTABLE HDU.");
13491
+ }
13492
+ const mocOrderItem = mocHDU.header.findById("MOCORDER");
13493
+ if (!mocOrderItem) {
13494
+ throw new Error("HiPS Moc.fits does not define MOCORDER.");
13495
+ }
13496
+ const maxOrder = Number(mocOrderItem.value);
13497
+ if (!Number.isInteger(maxOrder) || maxOrder < 0) {
13498
+ throw new Error(`Invalid MOCORDER value: ${mocOrderItem.value}`);
13499
+ }
13500
+ this._maxOrder = maxOrder;
13501
+ const intervals = [];
13502
+ for (let row = 0; row < mocHDU.rowCount; row++) {
13503
+ const rawUniq = mocHDU.getCell(row, "UNIQ");
13504
+ if (typeof rawUniq !== "number" ||
13505
+ !Number.isInteger(rawUniq) ||
13506
+ rawUniq < 4) {
13507
+ throw new Error(`Invalid MOC UNIQ value at row ${row}: ${String(rawUniq)}`);
13508
+ }
13509
+ const { order, pixel } = HiPSCoverage.decodeUniq(rawUniq);
13510
+ if (order > this._maxOrder) {
13511
+ throw new Error(`MOC cell order ${order} exceeds MOCORDER ${this._maxOrder}.`);
13512
+ }
13513
+ const scale = 4 ** (this._maxOrder - order);
13514
+ intervals.push({
13515
+ first: pixel * scale,
13516
+ last: (pixel + 1) * scale - 1,
13517
+ });
13518
+ }
13519
+ this._intervals = HiPSCoverage.mergeIntervals(intervals);
13520
+ this._loaded = true;
13521
+ }
13522
+ intersectsTile(order, pixel) {
13523
+ if (!this._loaded) {
13524
+ return true;
13525
+ }
13526
+ if (!Number.isInteger(order) ||
13527
+ order < 0 ||
13528
+ !Number.isInteger(pixel) ||
13529
+ pixel < 0) {
13530
+ return false;
13531
+ }
13532
+ let first;
13533
+ let last;
13534
+ if (order <= this._maxOrder) {
13535
+ const scale = 4 ** (this._maxOrder - order);
13536
+ first = pixel * scale;
13537
+ last = (pixel + 1) * scale - 1;
13538
+ }
13539
+ else {
13540
+ /*
13541
+ * At orders finer than the MOC resolution, reduce the tile
13542
+ * to its ancestor at MOCORDER.
13543
+ */
13544
+ const shift = 2 * (order - this._maxOrder);
13545
+ const ancestor = Math.floor(pixel / 2 ** shift);
13546
+ first = ancestor;
13547
+ last = ancestor;
13548
+ }
13549
+ return this.intersectsInterval(first, last);
13550
+ }
13551
+ intersectsInterval(first, last) {
13552
+ let low = 0;
13553
+ let high = this._intervals.length - 1;
13554
+ while (low <= high) {
13555
+ const middle = Math.floor((low + high) / 2);
13556
+ const interval = this._intervals[middle];
13557
+ if (interval.last < first) {
13558
+ low = middle + 1;
13559
+ continue;
13560
+ }
13561
+ if (interval.first > last) {
13562
+ high = middle - 1;
13563
+ continue;
13564
+ }
13565
+ return true;
13566
+ }
13567
+ return false;
13568
+ }
13569
+ static decodeUniq(uniq) {
13570
+ let order = 0;
13571
+ while (4 ** (order + 2) <= uniq) {
13572
+ order++;
13573
+ }
13574
+ const base = 4 ** (order + 1);
13575
+ return {
13576
+ order,
13577
+ pixel: uniq - base,
13578
+ };
13579
+ }
13580
+ static mergeIntervals(intervals) {
13581
+ if (intervals.length === 0) {
13582
+ return [];
13583
+ }
13584
+ intervals.sort((a, b) => a.first - b.first);
13585
+ const merged = [{ ...intervals[0] }];
13586
+ for (let i = 1; i < intervals.length; i++) {
13587
+ const current = intervals[i];
13588
+ const previous = merged[merged.length - 1];
13589
+ if (current.first <= previous.last + 1) {
13590
+ previous.last = Math.max(previous.last, current.last);
13591
+ }
13592
+ else {
13593
+ merged.push({ ...current });
13594
+ }
13595
+ }
13596
+ return merged;
13597
+ }
13598
+ }
13599
+ exports.HiPSCoverage = HiPSCoverage;
13600
+
13601
+
13102
13602
  /***/ },
13103
13603
 
13104
13604
  /***/ 5087
@@ -13302,7 +13802,7 @@ class Tile {
13302
13802
  vertexIndices = new Uint16Array();
13303
13803
  vertexIndexBuffer;
13304
13804
  _tileBuffer;
13305
- opacity = 1.0;
13805
+ // public opacity = 1.0;
13306
13806
  _webgl;
13307
13807
  _visibleTileManager;
13308
13808
  // private _hipsShaderProgram
@@ -13371,6 +13871,7 @@ class Tile {
13371
13871
  async loadFits() {
13372
13872
  try {
13373
13873
  this._fitsTile = await (0, FitsTileLoader_js_1.loadFitsTile)(this._texurl);
13874
+ this._hips.observeFITSDataRange(this._fitsTile);
13374
13875
  this._ready = true;
13375
13876
  }
13376
13877
  catch (error) {
@@ -13509,30 +14010,7 @@ class Tile {
13509
14010
  gl.bufferData(gl.ARRAY_BUFFER, this.vertexPosition[qidx], gl.STATIC_DRAW);
13510
14011
  }
13511
14012
  getFitsDataRange() {
13512
- const hipsRange = this._hips.dataRange;
13513
- if (hipsRange.min !== undefined &&
13514
- hipsRange.max !== undefined &&
13515
- Number.isFinite(hipsRange.min) &&
13516
- Number.isFinite(hipsRange.max) &&
13517
- hipsRange.max > hipsRange.min) {
13518
- return {
13519
- min: hipsRange.min,
13520
- max: hipsRange.max,
13521
- };
13522
- }
13523
- const fits = this._fitsTile;
13524
- if (fits &&
13525
- fits.dataMin !== null &&
13526
- fits.dataMax !== null &&
13527
- Number.isFinite(fits.dataMin) &&
13528
- Number.isFinite(fits.dataMax) &&
13529
- fits.dataMax > fits.dataMin) {
13530
- return {
13531
- min: fits.dataMin,
13532
- max: fits.dataMax,
13533
- };
13534
- }
13535
- return null;
14013
+ return this._hips.getFITSDisplayRange(this._fitsTile);
13536
14014
  }
13537
14015
  get inView() {
13538
14016
  return this._inView;
@@ -13636,7 +14114,7 @@ class Tile {
13636
14114
  console.error(`[Tile] No display range available for FITS tile ${this._texurl}`);
13637
14115
  return;
13638
14116
  }
13639
- hipsShaderProgram.setTextureDataMode(this._hipsShaderIndex, true, range.min, range.max);
14117
+ hipsShaderProgram.setTextureDataMode(this._hipsShaderIndex, true, range.min, range.max, this._hips.fitsStretch.scaleFunctionIndex, this._hips.fitsStretch.scaleParam);
13640
14118
  }
13641
14119
  else {
13642
14120
  hipsShaderProgram.setTextureDataMode(this._hipsShaderIndex, false);
@@ -13648,8 +14126,9 @@ class Tile {
13648
14126
  gl.bindTexture(gl.TEXTURE_2D, this._texture);
13649
14127
  const factorLocation = locations.textureAlpha[this._hipsShaderIndex];
13650
14128
  if (factorLocation) {
13651
- gl.uniform1f(factorLocation, this.opacity);
14129
+ gl.uniform1f(factorLocation, 1.0);
13652
14130
  }
14131
+ hipsShaderProgram.setLayerOpacity(this._hips.opacity);
13653
14132
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.vertexIndexBuffer);
13654
14133
  const elemno = this.vertexIndices.length;
13655
14134
  const indexType = this.vertexIndices instanceof Uint32Array
@@ -13673,6 +14152,9 @@ class Tile {
13673
14152
  const childTileNo = (this._tileno << 2) + c;
13674
14153
  const list = visibleTilesMap.get(childrenOrder);
13675
14154
  if (list.includes(childTileNo)) {
14155
+ if (!this._hips.intersectsCoverage(childrenOrder, childTileNo)) {
14156
+ continue;
14157
+ }
13676
14158
  const childTile = this._isGalacticHips
13677
14159
  ? this._tileBuffer.getGalTile(childTileNo, childrenOrder, this._hips)
13678
14160
  : this._tileBuffer.getTile(childTileNo, childrenOrder, this._hips);
@@ -13764,6 +14246,9 @@ class TileBuffer {
13764
14246
  if (order > hips.maxOrder) {
13765
14247
  continue;
13766
14248
  }
14249
+ if (!hips.intersectsCoverage(order, tileno)) {
14250
+ continue;
14251
+ }
13767
14252
  this.getTile(tileno, order, hips);
13768
14253
  }
13769
14254
  }
@@ -13773,6 +14258,9 @@ class TileBuffer {
13773
14258
  if (order > hips.maxOrder) {
13774
14259
  continue;
13775
14260
  }
14261
+ if (!hips.intersectsCoverage(order, tileno)) {
14262
+ continue;
14263
+ }
13776
14264
  this.getGalTile(tileno, order, hips);
13777
14265
  }
13778
14266
  }
@@ -16489,6 +16977,8 @@ class HiPSShaderProgram {
16489
16977
  textureMode: "uTextureMode",
16490
16978
  dataMin: "uDataMin",
16491
16979
  dataMax: "uDataMax",
16980
+ scaleFunction: "uScaleFunction",
16981
+ scaleParam: "uScaleParam",
16492
16982
  };
16493
16983
  this.gl_attributes = {
16494
16984
  vertex_pos: "aVertexPosition",
@@ -16503,6 +16993,9 @@ class HiPSShaderProgram {
16503
16993
  textureMode: new Array(8).fill(null),
16504
16994
  dataMin: new Array(8).fill(null),
16505
16995
  dataMax: new Array(8).fill(null),
16996
+ scaleFunction: new Array(8).fill(null),
16997
+ scaleParam: new Array(8).fill(null),
16998
+ layerOpacity: null,
16506
16999
  clorMapIdx: null,
16507
17000
  vertexPositionAttribute: -1,
16508
17001
  textureCoordAttribute: -1,
@@ -16518,6 +17011,13 @@ class HiPSShaderProgram {
16518
17011
  gl.useProgram(this._shaderProgram);
16519
17012
  return this._shaderProgram;
16520
17013
  }
17014
+ setLayerOpacity(opacity) {
17015
+ const gl = this._webgl;
17016
+ this.locations.layerOpacity = gl.getUniformLocation(this.shaderProgram, "uLayerOpacity");
17017
+ if (this.locations.layerOpacity !== null) {
17018
+ gl.uniform1f(this.locations.layerOpacity, opacity);
17019
+ }
17020
+ }
16521
17021
  setRuntimeColorMap(colorMap) {
16522
17022
  this._runtimeColorMap = colorMap;
16523
17023
  }
@@ -16628,7 +17128,7 @@ class HiPSShaderProgram {
16628
17128
  }
16629
17129
  gl.useProgram(this.shaderProgram);
16630
17130
  }
16631
- setTextureDataMode(samplerIndex, fits, min, max) {
17131
+ setTextureDataMode(samplerIndex, fits, min, max, scaleFunction = 0, scaleParam = 1) {
16632
17132
  if (samplerIndex < 0 || samplerIndex >= 8) {
16633
17133
  throw new Error(`Invalid HiPS sampler index: ${samplerIndex}`);
16634
17134
  }
@@ -16649,12 +17149,20 @@ class HiPSShaderProgram {
16649
17149
  }
16650
17150
  const minLocation = this.locations.dataMin[samplerIndex];
16651
17151
  const maxLocation = this.locations.dataMax[samplerIndex];
17152
+ const scaleFunctionLocation = this.locations.scaleFunction[samplerIndex];
17153
+ const scaleParamLocation = this.locations.scaleParam[samplerIndex];
16652
17154
  if (minLocation) {
16653
17155
  gl.uniform1f(minLocation, min);
16654
17156
  }
16655
17157
  if (maxLocation) {
16656
17158
  gl.uniform1f(maxLocation, max);
16657
17159
  }
17160
+ if (scaleFunctionLocation) {
17161
+ gl.uniform1i(scaleFunctionLocation, scaleFunction);
17162
+ }
17163
+ if (scaleParamLocation) {
17164
+ gl.uniform1f(scaleParamLocation, scaleParam);
17165
+ }
16658
17166
  }
16659
17167
  enableShaders(pMatrix, vMatrix, mMatrix, colorMapIdx) {
16660
17168
  // const gl = global.gl
@@ -16670,6 +17178,8 @@ class HiPSShaderProgram {
16670
17178
  this.locations.textureMode[i] = gl.getUniformLocation(this.shaderProgram, `uTextureMode${i}`);
16671
17179
  this.locations.dataMin[i] = gl.getUniformLocation(this.shaderProgram, `uDataMin${i}`);
16672
17180
  this.locations.dataMax[i] = gl.getUniformLocation(this.shaderProgram, `uDataMax${i}`);
17181
+ this.locations.scaleFunction[i] = gl.getUniformLocation(this.shaderProgram, `uScaleFunction${i}`);
17182
+ this.locations.scaleParam[i] = gl.getUniformLocation(this.shaderProgram, `uScaleParam${i}`);
16673
17183
  if (this.locations.samplers[i]) {
16674
17184
  gl.uniform1i(this.locations.samplers[i], i);
16675
17185
  }
@@ -17008,7 +17518,7 @@ class ShaderManager {
17008
17518
  }
17009
17519
  static footprintVS() {
17010
17520
  return `#version 300 es
17011
- precision highp float;
17521
+ precision mediump float;
17012
17522
 
17013
17523
  layout(location = 0) in vec4 aCatPosition;
17014
17524
 
@@ -17050,10 +17560,11 @@ class ShaderManager {
17050
17560
  }
17051
17561
  static hipsNativeFS() {
17052
17562
  return `#version 300 es
17053
- precision mediump float;
17563
+ precision highp float;
17054
17564
 
17055
17565
  in vec2 vTextureCoord;
17056
17566
 
17567
+ uniform float uLayerOpacity;
17057
17568
  uniform float uFactor0;
17058
17569
  uniform float uFactor1;
17059
17570
  uniform float uFactor2;
@@ -17110,15 +17621,16 @@ class ShaderManager {
17110
17621
  sampleHiPSTexture7(vTextureCoord).rgb * uFactor7;
17111
17622
  }
17112
17623
 
17113
- fragColor = vec4(finalColor, 1.0);
17624
+ fragColor = vec4(finalColor, uLayerOpacity);
17114
17625
  }`;
17115
17626
  }
17116
17627
  static hipsGrayscaleFS() {
17117
17628
  return `#version 300 es
17118
- precision mediump float;
17629
+ precision highp float;
17119
17630
 
17120
17631
  in vec2 vTextureCoord;
17121
17632
 
17633
+ uniform float uLayerOpacity;
17122
17634
  uniform float uFactor0;
17123
17635
  uniform float uFactor1;
17124
17636
  uniform float uFactor2;
@@ -17183,12 +17695,14 @@ class ShaderManager {
17183
17695
  finalColor += color7.rgb * uFactor7;
17184
17696
  }
17185
17697
 
17186
- fragColor = vec4(finalColor, 1.0);
17698
+ fragColor = vec4(finalColor, uLayerOpacity);
17187
17699
  }`;
17188
17700
  }
17189
17701
  static hipsColorMapFS() {
17190
17702
  return `#version 300 es
17191
- precision mediump float;
17703
+ precision highp float;
17704
+
17705
+ uniform float uLayerOpacity;
17192
17706
 
17193
17707
  in vec2 vTextureCoord;
17194
17708
 
@@ -17282,7 +17796,7 @@ class ShaderManager {
17282
17796
  ) * uFactor7;
17283
17797
  }
17284
17798
 
17285
- fragColor = vec4(finalColor, 1.0);
17799
+ fragColor = vec4(finalColor, uLayerOpacity);
17286
17800
  }`;
17287
17801
  }
17288
17802
  static hipsSamplerGLSL(index) {
@@ -17291,6 +17805,36 @@ class ShaderManager {
17291
17805
  uniform int uTextureMode${index};
17292
17806
  uniform float uDataMin${index};
17293
17807
  uniform float uDataMax${index};
17808
+ uniform int uScaleFunction${index};
17809
+ uniform float uScaleParam${index};
17810
+
17811
+ float asinhCompat${index}(float value) {
17812
+ return log(value + sqrt(value * value + 1.0));
17813
+ }
17814
+
17815
+ float applyFitsScale${index}(float normalized) {
17816
+ float x = clamp(normalized, 0.0, 1.0);
17817
+
17818
+ if (uScaleFunction${index} == 1) {
17819
+ return sqrt(x);
17820
+ }
17821
+
17822
+ if (uScaleFunction${index} == 2) {
17823
+ float strength = max(uScaleParam${index}, 1.0);
17824
+ return log(1.0 + strength * x) / log(1.0 + strength);
17825
+ }
17826
+
17827
+ if (uScaleFunction${index} == 3) {
17828
+ float strength = max(uScaleParam${index}, 1.0);
17829
+ return asinhCompat${index}(strength * x) / asinhCompat${index}(strength);
17830
+ }
17831
+
17832
+ if (uScaleFunction${index} == 4) {
17833
+ return pow(x, max(uScaleParam${index}, 0.0001));
17834
+ }
17835
+
17836
+ return x;
17837
+ }
17294
17838
 
17295
17839
  vec4 sampleHiPSTexture${index}(vec2 uv) {
17296
17840
  vec4 color = texture(uSampler${index}, uv);
@@ -17314,10 +17858,12 @@ class ShaderManager {
17314
17858
  1.0
17315
17859
  );
17316
17860
 
17861
+ float scaled = applyFitsScale${index}(normalized);
17862
+
17317
17863
  return vec4(
17318
- normalized,
17319
- normalized,
17320
- normalized,
17864
+ scaled,
17865
+ scaled,
17866
+ scaled,
17321
17867
  1.0
17322
17868
  );
17323
17869
  }
@@ -19822,11 +20368,12 @@ function findCircleThroughTwoPoints(points, q1, q2) {
19822
20368
 
19823
20369
  // EXPORTS
19824
20370
  __webpack_require__.d(__webpack_exports__, {
20371
+ BinaryTableHDU: () => (/* reexport */ BinaryTableHDU),
19825
20372
  FITSHeaderManager: () => (/* reexport */ FITSHeaderManager),
19826
20373
  FITSParser: () => (/* reexport */ FITSParser)
19827
20374
  });
19828
20375
 
19829
- // UNUSED EXPORTS: AsciiTableHDU, BinaryTableHDU, FITSFile, FITSHDU, FITSHeaderItem, FITSWriter, ImageHDU, ParseHeader, ParsePayload, PrimaryHDU
20376
+ // UNUSED EXPORTS: AsciiTableHDU, FITSFile, FITSHDU, FITSHeaderItem, FITSWriter, ImageHDU, ParseHeader, ParsePayload, PrimaryHDU
19830
20377
 
19831
20378
  ;// ./node_modules/jsfitsio/lib-esm/model/FITSHeaderItem.js
19832
20379
  /**