astro-viewer 3.8.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.
- package/README.md +325 -222
- package/dist/astroviewer.cjs +286 -86
- package/dist/astroviewer.cjs.map +1 -1
- package/dist/astroviewer.js +286 -86
- package/dist/astroviewer.js.map +1 -1
- package/dist/astroviewer.min.js +1 -1
- package/dist/astroviewer.min.js.map +1 -1
- package/lib-esm/AstroSphere.d.ts +4 -0
- package/lib-esm/AstroSphere.d.ts.map +1 -1
- package/lib-esm/AstroSphere.js +15 -0
- package/lib-esm/AstroSphere.js.map +1 -1
- package/lib-esm/AstroViewer.d.ts +4 -0
- package/lib-esm/AstroViewer.d.ts.map +1 -1
- package/lib-esm/AstroViewer.js +9 -0
- package/lib-esm/AstroViewer.js.map +1 -1
- package/lib-esm/index.d.ts +1 -0
- package/lib-esm/index.d.ts.map +1 -1
- package/lib-esm/index.js +0 -1
- package/lib-esm/index.js.map +1 -1
- package/lib-esm/model/grid/HealpixGrid.d.ts.map +1 -1
- package/lib-esm/model/grid/HealpixGrid.js +7 -1
- package/lib-esm/model/grid/HealpixGrid.js.map +1 -1
- package/lib-esm/model/hips/AllSky.d.ts.map +1 -1
- package/lib-esm/model/hips/AllSky.js +3 -25
- package/lib-esm/model/hips/AllSky.js.map +1 -1
- package/lib-esm/model/hips/AncestorTile.d.ts.map +1 -1
- package/lib-esm/model/hips/AncestorTile.js +3 -25
- package/lib-esm/model/hips/AncestorTile.js.map +1 -1
- package/lib-esm/model/hips/FitsTileLoader.d.ts +4 -0
- package/lib-esm/model/hips/FitsTileLoader.d.ts.map +1 -1
- package/lib-esm/model/hips/FitsTileLoader.js +39 -1
- package/lib-esm/model/hips/FitsTileLoader.js.map +1 -1
- package/lib-esm/model/hips/HiPS.d.ts +24 -0
- package/lib-esm/model/hips/HiPS.d.ts.map +1 -1
- package/lib-esm/model/hips/HiPS.js +153 -0
- package/lib-esm/model/hips/HiPS.js.map +1 -1
- package/lib-esm/model/hips/Tile.d.ts.map +1 -1
- package/lib-esm/model/hips/Tile.js +3 -25
- package/lib-esm/model/hips/Tile.js.map +1 -1
- package/lib-esm/shader/HiPSShaderProgram.d.ts +5 -1
- package/lib-esm/shader/HiPSShaderProgram.d.ts.map +1 -1
- package/lib-esm/shader/HiPSShaderProgram.js +15 -1
- package/lib-esm/shader/HiPSShaderProgram.js.map +1 -1
- package/lib-esm/shader/ShaderManager.d.ts.map +1 -1
- package/lib-esm/shader/ShaderManager.js +39 -7
- package/lib-esm/shader/ShaderManager.js.map +1 -1
- package/package.json +1 -1
package/dist/astroviewer.js
CHANGED
|
@@ -4141,6 +4141,21 @@ class AstroSphere {
|
|
|
4141
4141
|
}
|
|
4142
4142
|
this._activeHiPS.changeFormat(format);
|
|
4143
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
|
+
}
|
|
4144
4159
|
createHiPS(hipsDescriptor) {
|
|
4145
4160
|
return new HiPS_js_1.HiPS(1, [0.0, 0.0, 0.0], 0, 0, hipsDescriptor, this._webgl, this._healpixGrid);
|
|
4146
4161
|
}
|
|
@@ -5013,6 +5028,15 @@ class AstroViewer {
|
|
|
5013
5028
|
changeHiPSFormat(format) {
|
|
5014
5029
|
this.astroSphere.changeHiPSFormat(format);
|
|
5015
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
|
+
}
|
|
5016
5040
|
getActiveHiPSFormats() {
|
|
5017
5041
|
return this.astroSphere.activeHiPS?.availableFormats ?? [];
|
|
5018
5042
|
}
|
|
@@ -5883,7 +5907,6 @@ var Source_js_1 = __webpack_require__(146);
|
|
|
5883
5907
|
Object.defineProperty(exports, "Source", ({ enumerable: true, get: function () { return Source_js_1.Source; } }));
|
|
5884
5908
|
var Footprint_js_1 = __webpack_require__(2475);
|
|
5885
5909
|
Object.defineProperty(exports, "Footprint", ({ enumerable: true, get: function () { return Footprint_js_1.Footprint; } }));
|
|
5886
|
-
console.log('astroviewer UMD loaded');
|
|
5887
5910
|
|
|
5888
5911
|
|
|
5889
5912
|
/***/ },
|
|
@@ -11673,8 +11696,14 @@ class HealpixGrid extends AbstractSkyEntity_js_1.AbstractSkyEntity {
|
|
|
11673
11696
|
// FIX: pass model & pMatrix to match FoVUtils TS signature
|
|
11674
11697
|
const center = FoVUtils_js_1.FoVUtils.getCenterJ2000(gl.canvas, this, this._webgl, camera, pMatrix);
|
|
11675
11698
|
const fovMin = (this.getMinFoV() * Math.PI) / 180 / 2;
|
|
11699
|
+
const visibleHealpix = Global_js_1.default.getHealpix(this._visibleorder);
|
|
11700
|
+
const maxPixel = visibleHealpix.getNPix();
|
|
11676
11701
|
for (let p = 0; p < pixels.length; p++) {
|
|
11677
|
-
const
|
|
11702
|
+
const pixel = pixels[p];
|
|
11703
|
+
if (!Number.isInteger(pixel) || pixel < 0 || pixel >= maxPixel) {
|
|
11704
|
+
continue;
|
|
11705
|
+
}
|
|
11706
|
+
const pixCenter = visibleHealpix.pix2vec(pixel);
|
|
11678
11707
|
// const pixCenter = (global.getHealpix(global.order).pix2vec(pixels[p]) as BoundVec);
|
|
11679
11708
|
const point = new Point_js_1.Point({ x: pixCenter.x, y: pixCenter.y, z: pixCenter.z }, CoordsType_js_1.CoordsType.CARTESIAN);
|
|
11680
11709
|
const distance = GeomUtils_js_1.default.orthodromicDistance(center, point);
|
|
@@ -12104,6 +12133,7 @@ class AllSky {
|
|
|
12104
12133
|
async loadFits() {
|
|
12105
12134
|
try {
|
|
12106
12135
|
this._fitsTile = await (0, FitsTileLoader_js_1.loadFitsTile)(this._texurl);
|
|
12136
|
+
this._hips.observeFITSDataRange(this._fitsTile);
|
|
12107
12137
|
this.textureLoaded();
|
|
12108
12138
|
this.initModelBuffer();
|
|
12109
12139
|
this._textureLoaded = true;
|
|
@@ -12148,30 +12178,7 @@ class AllSky {
|
|
|
12148
12178
|
}
|
|
12149
12179
|
}
|
|
12150
12180
|
getFitsDataRange() {
|
|
12151
|
-
|
|
12152
|
-
if (hipsRange.min !== undefined &&
|
|
12153
|
-
hipsRange.max !== undefined &&
|
|
12154
|
-
Number.isFinite(hipsRange.min) &&
|
|
12155
|
-
Number.isFinite(hipsRange.max) &&
|
|
12156
|
-
hipsRange.max > hipsRange.min) {
|
|
12157
|
-
return {
|
|
12158
|
-
min: hipsRange.min,
|
|
12159
|
-
max: hipsRange.max,
|
|
12160
|
-
};
|
|
12161
|
-
}
|
|
12162
|
-
const fits = this._fitsTile;
|
|
12163
|
-
if (fits &&
|
|
12164
|
-
fits.dataMin !== null &&
|
|
12165
|
-
fits.dataMax !== null &&
|
|
12166
|
-
Number.isFinite(fits.dataMin) &&
|
|
12167
|
-
Number.isFinite(fits.dataMax) &&
|
|
12168
|
-
fits.dataMax > fits.dataMin) {
|
|
12169
|
-
return {
|
|
12170
|
-
min: fits.dataMin,
|
|
12171
|
-
max: fits.dataMax,
|
|
12172
|
-
};
|
|
12173
|
-
}
|
|
12174
|
-
return null;
|
|
12181
|
+
return this._hips.getFITSDisplayRange(this._fitsTile);
|
|
12175
12182
|
}
|
|
12176
12183
|
initModelBuffer() {
|
|
12177
12184
|
const gl = this._webgl;
|
|
@@ -12285,7 +12292,7 @@ class AllSky {
|
|
|
12285
12292
|
console.error(`[AllSky] No display range available for FITS atlas ${this._texurl}`);
|
|
12286
12293
|
return false;
|
|
12287
12294
|
}
|
|
12288
|
-
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);
|
|
12289
12296
|
}
|
|
12290
12297
|
else {
|
|
12291
12298
|
this._hipsShaderProgram.setTextureDataMode(this._hipsShaderIndex, false);
|
|
@@ -12423,6 +12430,7 @@ class AncestorTile {
|
|
|
12423
12430
|
async loadFits() {
|
|
12424
12431
|
try {
|
|
12425
12432
|
this._fitsTile = await (0, FitsTileLoader_js_1.loadFitsTile)(this._texurl);
|
|
12433
|
+
this._hips.observeFITSDataRange(this._fitsTile);
|
|
12426
12434
|
this.textureLoaded();
|
|
12427
12435
|
const fits = this._fitsTile;
|
|
12428
12436
|
if (!fits) {
|
|
@@ -12480,30 +12488,7 @@ class AncestorTile {
|
|
|
12480
12488
|
}
|
|
12481
12489
|
}
|
|
12482
12490
|
getFitsDataRange() {
|
|
12483
|
-
|
|
12484
|
-
if (hipsRange.min !== undefined &&
|
|
12485
|
-
hipsRange.max !== undefined &&
|
|
12486
|
-
Number.isFinite(hipsRange.min) &&
|
|
12487
|
-
Number.isFinite(hipsRange.max) &&
|
|
12488
|
-
hipsRange.max > hipsRange.min) {
|
|
12489
|
-
return {
|
|
12490
|
-
min: hipsRange.min,
|
|
12491
|
-
max: hipsRange.max,
|
|
12492
|
-
};
|
|
12493
|
-
}
|
|
12494
|
-
const fits = this._fitsTile;
|
|
12495
|
-
if (fits &&
|
|
12496
|
-
fits.dataMin !== null &&
|
|
12497
|
-
fits.dataMax !== null &&
|
|
12498
|
-
Number.isFinite(fits.dataMin) &&
|
|
12499
|
-
Number.isFinite(fits.dataMax) &&
|
|
12500
|
-
fits.dataMax > fits.dataMin) {
|
|
12501
|
-
return {
|
|
12502
|
-
min: fits.dataMin,
|
|
12503
|
-
max: fits.dataMax,
|
|
12504
|
-
};
|
|
12505
|
-
}
|
|
12506
|
-
return null;
|
|
12491
|
+
return this._hips.getFITSDisplayRange(this._fitsTile);
|
|
12507
12492
|
}
|
|
12508
12493
|
initModelBuffer() {
|
|
12509
12494
|
const gl = this._webgl;
|
|
@@ -12603,7 +12588,7 @@ class AncestorTile {
|
|
|
12603
12588
|
console.error(`[AncestorTile] No display range available for FITS tile ${this._texurl}`);
|
|
12604
12589
|
return false;
|
|
12605
12590
|
}
|
|
12606
|
-
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);
|
|
12607
12592
|
}
|
|
12608
12593
|
else {
|
|
12609
12594
|
this._hipsShaderProgram.setTextureDataMode(this._hipsShaderIndex, false);
|
|
@@ -12681,6 +12666,9 @@ var __webpack_unused_export__;
|
|
|
12681
12666
|
__webpack_unused_export__ = ({ value: true });
|
|
12682
12667
|
exports.loadFitsTile = loadFitsTile;
|
|
12683
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;
|
|
12684
12672
|
async function loadFitsTile(url) {
|
|
12685
12673
|
const fitsFile = await jsfitsio_1.FITSParser.loadFITSFile(url);
|
|
12686
12674
|
if (!fitsFile?.primaryHDU) {
|
|
@@ -12710,22 +12698,57 @@ async function loadFitsTile(url) {
|
|
|
12710
12698
|
const dataMin = getNumericHeaderValue(image.header, jsfitsio_1.FITSHeaderManager.DATAMIN);
|
|
12711
12699
|
const dataMax = getNumericHeaderValue(image.header, jsfitsio_1.FITSHeaderManager.DATAMAX);
|
|
12712
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));
|
|
12713
12705
|
for (let i = 0; i < data.length; i++) {
|
|
12714
12706
|
const rawValue = data[i];
|
|
12715
12707
|
if (!Number.isFinite(rawValue) || (blank !== null && rawValue === blank)) {
|
|
12716
12708
|
pixels[i] = Number.NaN;
|
|
12717
12709
|
continue;
|
|
12718
12710
|
}
|
|
12719
|
-
|
|
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
|
+
}
|
|
12720
12722
|
}
|
|
12723
|
+
const robustRange = computeRobustRange(robustSamples);
|
|
12721
12724
|
return {
|
|
12722
12725
|
width,
|
|
12723
12726
|
height,
|
|
12724
12727
|
pixels,
|
|
12725
12728
|
dataMin,
|
|
12726
12729
|
dataMax,
|
|
12730
|
+
physicalMin,
|
|
12731
|
+
physicalMax,
|
|
12732
|
+
robustMin: robustRange?.min ?? null,
|
|
12733
|
+
robustMax: robustRange?.max ?? null,
|
|
12727
12734
|
};
|
|
12728
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
|
+
}
|
|
12729
12752
|
function getNumericHeaderValue(header, key) {
|
|
12730
12753
|
const item = header.findById(key);
|
|
12731
12754
|
if (!item) {
|
|
@@ -12931,6 +12954,21 @@ const ColorMaps_js_1 = __importDefault(__webpack_require__(619));
|
|
|
12931
12954
|
const AncestorTile_js_1 = __importDefault(__webpack_require__(2885));
|
|
12932
12955
|
const AllSky_js_1 = __importDefault(__webpack_require__(2368));
|
|
12933
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;
|
|
12934
12972
|
class HiPS extends AbstractSkyEntity_js_1.AbstractSkyEntity {
|
|
12935
12973
|
_ancestorTiles;
|
|
12936
12974
|
_allSkyTile;
|
|
@@ -12947,6 +12985,13 @@ class HiPS extends AbstractSkyEntity_js_1.AbstractSkyEntity {
|
|
|
12947
12985
|
samplerIdx = 0;
|
|
12948
12986
|
colorMapIdx = 0;
|
|
12949
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 = [];
|
|
12950
12995
|
_healpixGrid;
|
|
12951
12996
|
// exposed read-only helpers
|
|
12952
12997
|
get maxOrder() {
|
|
@@ -13182,6 +13227,137 @@ class HiPS extends AbstractSkyEntity_js_1.AbstractSkyEntity {
|
|
|
13182
13227
|
get dataRange() {
|
|
13183
13228
|
return this._descriptor.dataRange;
|
|
13184
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
|
+
}
|
|
13185
13361
|
getCurrentHealpixOrder() {
|
|
13186
13362
|
return this._visibleorder;
|
|
13187
13363
|
}
|
|
@@ -13695,6 +13871,7 @@ class Tile {
|
|
|
13695
13871
|
async loadFits() {
|
|
13696
13872
|
try {
|
|
13697
13873
|
this._fitsTile = await (0, FitsTileLoader_js_1.loadFitsTile)(this._texurl);
|
|
13874
|
+
this._hips.observeFITSDataRange(this._fitsTile);
|
|
13698
13875
|
this._ready = true;
|
|
13699
13876
|
}
|
|
13700
13877
|
catch (error) {
|
|
@@ -13833,30 +14010,7 @@ class Tile {
|
|
|
13833
14010
|
gl.bufferData(gl.ARRAY_BUFFER, this.vertexPosition[qidx], gl.STATIC_DRAW);
|
|
13834
14011
|
}
|
|
13835
14012
|
getFitsDataRange() {
|
|
13836
|
-
|
|
13837
|
-
if (hipsRange.min !== undefined &&
|
|
13838
|
-
hipsRange.max !== undefined &&
|
|
13839
|
-
Number.isFinite(hipsRange.min) &&
|
|
13840
|
-
Number.isFinite(hipsRange.max) &&
|
|
13841
|
-
hipsRange.max > hipsRange.min) {
|
|
13842
|
-
return {
|
|
13843
|
-
min: hipsRange.min,
|
|
13844
|
-
max: hipsRange.max,
|
|
13845
|
-
};
|
|
13846
|
-
}
|
|
13847
|
-
const fits = this._fitsTile;
|
|
13848
|
-
if (fits &&
|
|
13849
|
-
fits.dataMin !== null &&
|
|
13850
|
-
fits.dataMax !== null &&
|
|
13851
|
-
Number.isFinite(fits.dataMin) &&
|
|
13852
|
-
Number.isFinite(fits.dataMax) &&
|
|
13853
|
-
fits.dataMax > fits.dataMin) {
|
|
13854
|
-
return {
|
|
13855
|
-
min: fits.dataMin,
|
|
13856
|
-
max: fits.dataMax,
|
|
13857
|
-
};
|
|
13858
|
-
}
|
|
13859
|
-
return null;
|
|
14013
|
+
return this._hips.getFITSDisplayRange(this._fitsTile);
|
|
13860
14014
|
}
|
|
13861
14015
|
get inView() {
|
|
13862
14016
|
return this._inView;
|
|
@@ -13960,7 +14114,7 @@ class Tile {
|
|
|
13960
14114
|
console.error(`[Tile] No display range available for FITS tile ${this._texurl}`);
|
|
13961
14115
|
return;
|
|
13962
14116
|
}
|
|
13963
|
-
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);
|
|
13964
14118
|
}
|
|
13965
14119
|
else {
|
|
13966
14120
|
hipsShaderProgram.setTextureDataMode(this._hipsShaderIndex, false);
|
|
@@ -16823,6 +16977,8 @@ class HiPSShaderProgram {
|
|
|
16823
16977
|
textureMode: "uTextureMode",
|
|
16824
16978
|
dataMin: "uDataMin",
|
|
16825
16979
|
dataMax: "uDataMax",
|
|
16980
|
+
scaleFunction: "uScaleFunction",
|
|
16981
|
+
scaleParam: "uScaleParam",
|
|
16826
16982
|
};
|
|
16827
16983
|
this.gl_attributes = {
|
|
16828
16984
|
vertex_pos: "aVertexPosition",
|
|
@@ -16837,6 +16993,8 @@ class HiPSShaderProgram {
|
|
|
16837
16993
|
textureMode: new Array(8).fill(null),
|
|
16838
16994
|
dataMin: new Array(8).fill(null),
|
|
16839
16995
|
dataMax: new Array(8).fill(null),
|
|
16996
|
+
scaleFunction: new Array(8).fill(null),
|
|
16997
|
+
scaleParam: new Array(8).fill(null),
|
|
16840
16998
|
layerOpacity: null,
|
|
16841
16999
|
clorMapIdx: null,
|
|
16842
17000
|
vertexPositionAttribute: -1,
|
|
@@ -16970,7 +17128,7 @@ class HiPSShaderProgram {
|
|
|
16970
17128
|
}
|
|
16971
17129
|
gl.useProgram(this.shaderProgram);
|
|
16972
17130
|
}
|
|
16973
|
-
setTextureDataMode(samplerIndex, fits, min, max) {
|
|
17131
|
+
setTextureDataMode(samplerIndex, fits, min, max, scaleFunction = 0, scaleParam = 1) {
|
|
16974
17132
|
if (samplerIndex < 0 || samplerIndex >= 8) {
|
|
16975
17133
|
throw new Error(`Invalid HiPS sampler index: ${samplerIndex}`);
|
|
16976
17134
|
}
|
|
@@ -16991,12 +17149,20 @@ class HiPSShaderProgram {
|
|
|
16991
17149
|
}
|
|
16992
17150
|
const minLocation = this.locations.dataMin[samplerIndex];
|
|
16993
17151
|
const maxLocation = this.locations.dataMax[samplerIndex];
|
|
17152
|
+
const scaleFunctionLocation = this.locations.scaleFunction[samplerIndex];
|
|
17153
|
+
const scaleParamLocation = this.locations.scaleParam[samplerIndex];
|
|
16994
17154
|
if (minLocation) {
|
|
16995
17155
|
gl.uniform1f(minLocation, min);
|
|
16996
17156
|
}
|
|
16997
17157
|
if (maxLocation) {
|
|
16998
17158
|
gl.uniform1f(maxLocation, max);
|
|
16999
17159
|
}
|
|
17160
|
+
if (scaleFunctionLocation) {
|
|
17161
|
+
gl.uniform1i(scaleFunctionLocation, scaleFunction);
|
|
17162
|
+
}
|
|
17163
|
+
if (scaleParamLocation) {
|
|
17164
|
+
gl.uniform1f(scaleParamLocation, scaleParam);
|
|
17165
|
+
}
|
|
17000
17166
|
}
|
|
17001
17167
|
enableShaders(pMatrix, vMatrix, mMatrix, colorMapIdx) {
|
|
17002
17168
|
// const gl = global.gl
|
|
@@ -17012,6 +17178,8 @@ class HiPSShaderProgram {
|
|
|
17012
17178
|
this.locations.textureMode[i] = gl.getUniformLocation(this.shaderProgram, `uTextureMode${i}`);
|
|
17013
17179
|
this.locations.dataMin[i] = gl.getUniformLocation(this.shaderProgram, `uDataMin${i}`);
|
|
17014
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}`);
|
|
17015
17183
|
if (this.locations.samplers[i]) {
|
|
17016
17184
|
gl.uniform1i(this.locations.samplers[i], i);
|
|
17017
17185
|
}
|
|
@@ -17350,7 +17518,7 @@ class ShaderManager {
|
|
|
17350
17518
|
}
|
|
17351
17519
|
static footprintVS() {
|
|
17352
17520
|
return `#version 300 es
|
|
17353
|
-
precision
|
|
17521
|
+
precision mediump float;
|
|
17354
17522
|
|
|
17355
17523
|
layout(location = 0) in vec4 aCatPosition;
|
|
17356
17524
|
|
|
@@ -17392,7 +17560,7 @@ class ShaderManager {
|
|
|
17392
17560
|
}
|
|
17393
17561
|
static hipsNativeFS() {
|
|
17394
17562
|
return `#version 300 es
|
|
17395
|
-
precision
|
|
17563
|
+
precision highp float;
|
|
17396
17564
|
|
|
17397
17565
|
in vec2 vTextureCoord;
|
|
17398
17566
|
|
|
@@ -17458,7 +17626,7 @@ class ShaderManager {
|
|
|
17458
17626
|
}
|
|
17459
17627
|
static hipsGrayscaleFS() {
|
|
17460
17628
|
return `#version 300 es
|
|
17461
|
-
precision
|
|
17629
|
+
precision highp float;
|
|
17462
17630
|
|
|
17463
17631
|
in vec2 vTextureCoord;
|
|
17464
17632
|
|
|
@@ -17532,7 +17700,7 @@ class ShaderManager {
|
|
|
17532
17700
|
}
|
|
17533
17701
|
static hipsColorMapFS() {
|
|
17534
17702
|
return `#version 300 es
|
|
17535
|
-
precision
|
|
17703
|
+
precision highp float;
|
|
17536
17704
|
|
|
17537
17705
|
uniform float uLayerOpacity;
|
|
17538
17706
|
|
|
@@ -17637,6 +17805,36 @@ class ShaderManager {
|
|
|
17637
17805
|
uniform int uTextureMode${index};
|
|
17638
17806
|
uniform float uDataMin${index};
|
|
17639
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
|
+
}
|
|
17640
17838
|
|
|
17641
17839
|
vec4 sampleHiPSTexture${index}(vec2 uv) {
|
|
17642
17840
|
vec4 color = texture(uSampler${index}, uv);
|
|
@@ -17660,10 +17858,12 @@ class ShaderManager {
|
|
|
17660
17858
|
1.0
|
|
17661
17859
|
);
|
|
17662
17860
|
|
|
17861
|
+
float scaled = applyFitsScale${index}(normalized);
|
|
17862
|
+
|
|
17663
17863
|
return vec4(
|
|
17664
|
-
|
|
17665
|
-
|
|
17666
|
-
|
|
17864
|
+
scaled,
|
|
17865
|
+
scaled,
|
|
17866
|
+
scaled,
|
|
17667
17867
|
1.0
|
|
17668
17868
|
);
|
|
17669
17869
|
}
|