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.cjs
CHANGED
|
@@ -4130,6 +4130,21 @@ class AstroSphere {
|
|
|
4130
4130
|
}
|
|
4131
4131
|
this._activeHiPS.changeFormat(format);
|
|
4132
4132
|
}
|
|
4133
|
+
setHiPSFITSScaleFunction(scaleFunction, scaleParam) {
|
|
4134
|
+
if (!this._activeHiPS) {
|
|
4135
|
+
throw new Error("No active HiPS.");
|
|
4136
|
+
}
|
|
4137
|
+
this._activeHiPS.setFITSScaleFunction(scaleFunction, scaleParam);
|
|
4138
|
+
}
|
|
4139
|
+
getActiveHiPSFITSStretch() {
|
|
4140
|
+
return this._activeHiPS?.fitsStretch ?? null;
|
|
4141
|
+
}
|
|
4142
|
+
setHiPSFITSRangeMode(rangeMode) {
|
|
4143
|
+
if (!this._activeHiPS) {
|
|
4144
|
+
throw new Error("No active HiPS.");
|
|
4145
|
+
}
|
|
4146
|
+
this._activeHiPS.setFITSRangeMode(rangeMode);
|
|
4147
|
+
}
|
|
4133
4148
|
createHiPS(hipsDescriptor) {
|
|
4134
4149
|
return new HiPS_js_1.HiPS(1, [0.0, 0.0, 0.0], 0, 0, hipsDescriptor, this._webgl, this._healpixGrid);
|
|
4135
4150
|
}
|
|
@@ -5001,6 +5016,15 @@ class AstroViewer {
|
|
|
5001
5016
|
changeHiPSFormat(format) {
|
|
5002
5017
|
this.astroSphere.changeHiPSFormat(format);
|
|
5003
5018
|
}
|
|
5019
|
+
setHiPSFITSScaleFunction(scaleFunction, scaleParam) {
|
|
5020
|
+
this.astroSphere.setHiPSFITSScaleFunction(scaleFunction, scaleParam);
|
|
5021
|
+
}
|
|
5022
|
+
getActiveHiPSFITSStretch() {
|
|
5023
|
+
return this.astroSphere.getActiveHiPSFITSStretch();
|
|
5024
|
+
}
|
|
5025
|
+
setHiPSFITSRangeMode(rangeMode) {
|
|
5026
|
+
this.astroSphere.setHiPSFITSRangeMode(rangeMode);
|
|
5027
|
+
}
|
|
5004
5028
|
getActiveHiPSFormats() {
|
|
5005
5029
|
return this.astroSphere.activeHiPS?.availableFormats ?? [];
|
|
5006
5030
|
}
|
|
@@ -5867,7 +5891,6 @@ var Source_js_1 = __webpack_require__(146);
|
|
|
5867
5891
|
Object.defineProperty(exports, "Source", ({ enumerable: true, get: function () { return Source_js_1.Source; } }));
|
|
5868
5892
|
var Footprint_js_1 = __webpack_require__(2475);
|
|
5869
5893
|
Object.defineProperty(exports, "Footprint", ({ enumerable: true, get: function () { return Footprint_js_1.Footprint; } }));
|
|
5870
|
-
console.log('astroviewer UMD loaded');
|
|
5871
5894
|
|
|
5872
5895
|
|
|
5873
5896
|
/***/ },
|
|
@@ -11633,8 +11656,14 @@ class HealpixGrid extends AbstractSkyEntity_js_1.AbstractSkyEntity {
|
|
|
11633
11656
|
// FIX: pass model & pMatrix to match FoVUtils TS signature
|
|
11634
11657
|
const center = FoVUtils_js_1.FoVUtils.getCenterJ2000(gl.canvas, this, this._webgl, camera, pMatrix);
|
|
11635
11658
|
const fovMin = (this.getMinFoV() * Math.PI) / 180 / 2;
|
|
11659
|
+
const visibleHealpix = Global_js_1.default.getHealpix(this._visibleorder);
|
|
11660
|
+
const maxPixel = visibleHealpix.getNPix();
|
|
11636
11661
|
for (let p = 0; p < pixels.length; p++) {
|
|
11637
|
-
const
|
|
11662
|
+
const pixel = pixels[p];
|
|
11663
|
+
if (!Number.isInteger(pixel) || pixel < 0 || pixel >= maxPixel) {
|
|
11664
|
+
continue;
|
|
11665
|
+
}
|
|
11666
|
+
const pixCenter = visibleHealpix.pix2vec(pixel);
|
|
11638
11667
|
// const pixCenter = (global.getHealpix(global.order).pix2vec(pixels[p]) as BoundVec);
|
|
11639
11668
|
const point = new Point_js_1.Point({ x: pixCenter.x, y: pixCenter.y, z: pixCenter.z }, CoordsType_js_1.CoordsType.CARTESIAN);
|
|
11640
11669
|
const distance = GeomUtils_js_1.default.orthodromicDistance(center, point);
|
|
@@ -12062,6 +12091,7 @@ class AllSky {
|
|
|
12062
12091
|
async loadFits() {
|
|
12063
12092
|
try {
|
|
12064
12093
|
this._fitsTile = await (0, FitsTileLoader_js_1.loadFitsTile)(this._texurl);
|
|
12094
|
+
this._hips.observeFITSDataRange(this._fitsTile);
|
|
12065
12095
|
this.textureLoaded();
|
|
12066
12096
|
this.initModelBuffer();
|
|
12067
12097
|
this._textureLoaded = true;
|
|
@@ -12106,30 +12136,7 @@ class AllSky {
|
|
|
12106
12136
|
}
|
|
12107
12137
|
}
|
|
12108
12138
|
getFitsDataRange() {
|
|
12109
|
-
|
|
12110
|
-
if (hipsRange.min !== undefined &&
|
|
12111
|
-
hipsRange.max !== undefined &&
|
|
12112
|
-
Number.isFinite(hipsRange.min) &&
|
|
12113
|
-
Number.isFinite(hipsRange.max) &&
|
|
12114
|
-
hipsRange.max > hipsRange.min) {
|
|
12115
|
-
return {
|
|
12116
|
-
min: hipsRange.min,
|
|
12117
|
-
max: hipsRange.max,
|
|
12118
|
-
};
|
|
12119
|
-
}
|
|
12120
|
-
const fits = this._fitsTile;
|
|
12121
|
-
if (fits &&
|
|
12122
|
-
fits.dataMin !== null &&
|
|
12123
|
-
fits.dataMax !== null &&
|
|
12124
|
-
Number.isFinite(fits.dataMin) &&
|
|
12125
|
-
Number.isFinite(fits.dataMax) &&
|
|
12126
|
-
fits.dataMax > fits.dataMin) {
|
|
12127
|
-
return {
|
|
12128
|
-
min: fits.dataMin,
|
|
12129
|
-
max: fits.dataMax,
|
|
12130
|
-
};
|
|
12131
|
-
}
|
|
12132
|
-
return null;
|
|
12139
|
+
return this._hips.getFITSDisplayRange(this._fitsTile);
|
|
12133
12140
|
}
|
|
12134
12141
|
initModelBuffer() {
|
|
12135
12142
|
const gl = this._webgl;
|
|
@@ -12243,7 +12250,7 @@ class AllSky {
|
|
|
12243
12250
|
console.error(`[AllSky] No display range available for FITS atlas ${this._texurl}`);
|
|
12244
12251
|
return false;
|
|
12245
12252
|
}
|
|
12246
|
-
this._hipsShaderProgram.setTextureDataMode(this._hipsShaderIndex, true, range.min, range.max);
|
|
12253
|
+
this._hipsShaderProgram.setTextureDataMode(this._hipsShaderIndex, true, range.min, range.max, this._hips.fitsStretch.scaleFunctionIndex, this._hips.fitsStretch.scaleParam);
|
|
12247
12254
|
}
|
|
12248
12255
|
else {
|
|
12249
12256
|
this._hipsShaderProgram.setTextureDataMode(this._hipsShaderIndex, false);
|
|
@@ -12380,6 +12387,7 @@ class AncestorTile {
|
|
|
12380
12387
|
async loadFits() {
|
|
12381
12388
|
try {
|
|
12382
12389
|
this._fitsTile = await (0, FitsTileLoader_js_1.loadFitsTile)(this._texurl);
|
|
12390
|
+
this._hips.observeFITSDataRange(this._fitsTile);
|
|
12383
12391
|
this.textureLoaded();
|
|
12384
12392
|
const fits = this._fitsTile;
|
|
12385
12393
|
if (!fits) {
|
|
@@ -12437,30 +12445,7 @@ class AncestorTile {
|
|
|
12437
12445
|
}
|
|
12438
12446
|
}
|
|
12439
12447
|
getFitsDataRange() {
|
|
12440
|
-
|
|
12441
|
-
if (hipsRange.min !== undefined &&
|
|
12442
|
-
hipsRange.max !== undefined &&
|
|
12443
|
-
Number.isFinite(hipsRange.min) &&
|
|
12444
|
-
Number.isFinite(hipsRange.max) &&
|
|
12445
|
-
hipsRange.max > hipsRange.min) {
|
|
12446
|
-
return {
|
|
12447
|
-
min: hipsRange.min,
|
|
12448
|
-
max: hipsRange.max,
|
|
12449
|
-
};
|
|
12450
|
-
}
|
|
12451
|
-
const fits = this._fitsTile;
|
|
12452
|
-
if (fits &&
|
|
12453
|
-
fits.dataMin !== null &&
|
|
12454
|
-
fits.dataMax !== null &&
|
|
12455
|
-
Number.isFinite(fits.dataMin) &&
|
|
12456
|
-
Number.isFinite(fits.dataMax) &&
|
|
12457
|
-
fits.dataMax > fits.dataMin) {
|
|
12458
|
-
return {
|
|
12459
|
-
min: fits.dataMin,
|
|
12460
|
-
max: fits.dataMax,
|
|
12461
|
-
};
|
|
12462
|
-
}
|
|
12463
|
-
return null;
|
|
12448
|
+
return this._hips.getFITSDisplayRange(this._fitsTile);
|
|
12464
12449
|
}
|
|
12465
12450
|
initModelBuffer() {
|
|
12466
12451
|
const gl = this._webgl;
|
|
@@ -12560,7 +12545,7 @@ class AncestorTile {
|
|
|
12560
12545
|
console.error(`[AncestorTile] No display range available for FITS tile ${this._texurl}`);
|
|
12561
12546
|
return false;
|
|
12562
12547
|
}
|
|
12563
|
-
this._hipsShaderProgram.setTextureDataMode(this._hipsShaderIndex, true, range.min, range.max);
|
|
12548
|
+
this._hipsShaderProgram.setTextureDataMode(this._hipsShaderIndex, true, range.min, range.max, this._hips.fitsStretch.scaleFunctionIndex, this._hips.fitsStretch.scaleParam);
|
|
12564
12549
|
}
|
|
12565
12550
|
else {
|
|
12566
12551
|
this._hipsShaderProgram.setTextureDataMode(this._hipsShaderIndex, false);
|
|
@@ -12637,6 +12622,9 @@ var __webpack_unused_export__;
|
|
|
12637
12622
|
__webpack_unused_export__ = ({ value: true });
|
|
12638
12623
|
exports.loadFitsTile = loadFitsTile;
|
|
12639
12624
|
const jsfitsio_1 = __webpack_require__(4971);
|
|
12625
|
+
const ROBUST_SAMPLE_COUNT = 8192;
|
|
12626
|
+
const ROBUST_LOW_PERCENTILE = 0.005;
|
|
12627
|
+
const ROBUST_HIGH_PERCENTILE = 0.995;
|
|
12640
12628
|
async function loadFitsTile(url) {
|
|
12641
12629
|
const fitsFile = await jsfitsio_1.FITSParser.loadFITSFile(url);
|
|
12642
12630
|
if (!fitsFile?.primaryHDU) {
|
|
@@ -12666,22 +12654,57 @@ async function loadFitsTile(url) {
|
|
|
12666
12654
|
const dataMin = getNumericHeaderValue(image.header, jsfitsio_1.FITSHeaderManager.DATAMIN);
|
|
12667
12655
|
const dataMax = getNumericHeaderValue(image.header, jsfitsio_1.FITSHeaderManager.DATAMAX);
|
|
12668
12656
|
const pixels = new Float32Array(data.length);
|
|
12657
|
+
let physicalMin = null;
|
|
12658
|
+
let physicalMax = null;
|
|
12659
|
+
const robustSamples = [];
|
|
12660
|
+
const sampleStep = Math.max(1, Math.floor(data.length / ROBUST_SAMPLE_COUNT));
|
|
12669
12661
|
for (let i = 0; i < data.length; i++) {
|
|
12670
12662
|
const rawValue = data[i];
|
|
12671
12663
|
if (!Number.isFinite(rawValue) || (blank !== null && rawValue === blank)) {
|
|
12672
12664
|
pixels[i] = Number.NaN;
|
|
12673
12665
|
continue;
|
|
12674
12666
|
}
|
|
12675
|
-
|
|
12667
|
+
const physicalValue = rawValue * bscale + bzero;
|
|
12668
|
+
pixels[i] = physicalValue;
|
|
12669
|
+
if (Number.isFinite(physicalValue)) {
|
|
12670
|
+
physicalMin =
|
|
12671
|
+
physicalMin === null ? physicalValue : Math.min(physicalMin, physicalValue);
|
|
12672
|
+
physicalMax =
|
|
12673
|
+
physicalMax === null ? physicalValue : Math.max(physicalMax, physicalValue);
|
|
12674
|
+
if (i % sampleStep === 0) {
|
|
12675
|
+
robustSamples.push(physicalValue);
|
|
12676
|
+
}
|
|
12677
|
+
}
|
|
12676
12678
|
}
|
|
12679
|
+
const robustRange = computeRobustRange(robustSamples);
|
|
12677
12680
|
return {
|
|
12678
12681
|
width,
|
|
12679
12682
|
height,
|
|
12680
12683
|
pixels,
|
|
12681
12684
|
dataMin,
|
|
12682
12685
|
dataMax,
|
|
12686
|
+
physicalMin,
|
|
12687
|
+
physicalMax,
|
|
12688
|
+
robustMin: robustRange?.min ?? null,
|
|
12689
|
+
robustMax: robustRange?.max ?? null,
|
|
12683
12690
|
};
|
|
12684
12691
|
}
|
|
12692
|
+
function computeRobustRange(samples) {
|
|
12693
|
+
if (samples.length === 0) {
|
|
12694
|
+
return null;
|
|
12695
|
+
}
|
|
12696
|
+
samples.sort((a, b) => a - b);
|
|
12697
|
+
const min = percentile(samples, ROBUST_LOW_PERCENTILE);
|
|
12698
|
+
const max = percentile(samples, ROBUST_HIGH_PERCENTILE);
|
|
12699
|
+
if (!Number.isFinite(min) || !Number.isFinite(max) || max <= min) {
|
|
12700
|
+
return null;
|
|
12701
|
+
}
|
|
12702
|
+
return { min, max };
|
|
12703
|
+
}
|
|
12704
|
+
function percentile(sortedValues, p) {
|
|
12705
|
+
const index = Math.min(sortedValues.length - 1, Math.max(0, Math.round((sortedValues.length - 1) * p)));
|
|
12706
|
+
return sortedValues[index];
|
|
12707
|
+
}
|
|
12685
12708
|
function getNumericHeaderValue(header, key) {
|
|
12686
12709
|
const item = header.findById(key);
|
|
12687
12710
|
if (!item) {
|
|
@@ -12885,6 +12908,21 @@ const ColorMaps_js_1 = __importDefault(__webpack_require__(619));
|
|
|
12885
12908
|
const AncestorTile_js_1 = __importDefault(__webpack_require__(2885));
|
|
12886
12909
|
const AllSky_js_1 = __importDefault(__webpack_require__(2368));
|
|
12887
12910
|
const HiPSCoverage_js_1 = __webpack_require__(9912);
|
|
12911
|
+
const FITS_SCALE_FUNCTION_INDEX = {
|
|
12912
|
+
linear: 0,
|
|
12913
|
+
sqrt: 1,
|
|
12914
|
+
log: 2,
|
|
12915
|
+
asinh: 3,
|
|
12916
|
+
gamma: 4,
|
|
12917
|
+
};
|
|
12918
|
+
const DEFAULT_FITS_SCALE_PARAM = {
|
|
12919
|
+
linear: 1,
|
|
12920
|
+
sqrt: 1,
|
|
12921
|
+
log: 100,
|
|
12922
|
+
asinh: 10,
|
|
12923
|
+
gamma: 0.5,
|
|
12924
|
+
};
|
|
12925
|
+
const MAX_ROBUST_TILE_RANGES = 512;
|
|
12888
12926
|
class HiPS extends AbstractSkyEntity_js_1.AbstractSkyEntity {
|
|
12889
12927
|
_ancestorTiles;
|
|
12890
12928
|
_allSkyTile;
|
|
@@ -12901,6 +12939,13 @@ class HiPS extends AbstractSkyEntity_js_1.AbstractSkyEntity {
|
|
|
12901
12939
|
samplerIdx = 0;
|
|
12902
12940
|
colorMapIdx = 0;
|
|
12903
12941
|
colorMap = ColorMaps_js_1.default["native"];
|
|
12942
|
+
_fitsStretch = {
|
|
12943
|
+
scaleFunction: "linear",
|
|
12944
|
+
scaleFunctionIndex: FITS_SCALE_FUNCTION_INDEX.linear,
|
|
12945
|
+
scaleParam: DEFAULT_FITS_SCALE_PARAM.linear,
|
|
12946
|
+
rangeMode: "robust",
|
|
12947
|
+
};
|
|
12948
|
+
_robustFITSRanges = [];
|
|
12904
12949
|
_healpixGrid;
|
|
12905
12950
|
// exposed read-only helpers
|
|
12906
12951
|
get maxOrder() {
|
|
@@ -13136,6 +13181,137 @@ class HiPS extends AbstractSkyEntity_js_1.AbstractSkyEntity {
|
|
|
13136
13181
|
get dataRange() {
|
|
13137
13182
|
return this._descriptor.dataRange;
|
|
13138
13183
|
}
|
|
13184
|
+
get fitsStretch() {
|
|
13185
|
+
return this._fitsStretch;
|
|
13186
|
+
}
|
|
13187
|
+
setFITSScaleFunction(scaleFunction, scaleParam) {
|
|
13188
|
+
const scaleFunctionIndex = FITS_SCALE_FUNCTION_INDEX[scaleFunction];
|
|
13189
|
+
if (scaleFunctionIndex === undefined) {
|
|
13190
|
+
throw new Error(`Unsupported FITS scale function: ${scaleFunction}`);
|
|
13191
|
+
}
|
|
13192
|
+
const defaultParam = DEFAULT_FITS_SCALE_PARAM[scaleFunction];
|
|
13193
|
+
const param = scaleParam ?? defaultParam;
|
|
13194
|
+
if (!Number.isFinite(param) || param <= 0) {
|
|
13195
|
+
throw new Error(`Invalid FITS scale parameter: ${param}`);
|
|
13196
|
+
}
|
|
13197
|
+
this._fitsStretch = {
|
|
13198
|
+
...this._fitsStretch,
|
|
13199
|
+
scaleFunction,
|
|
13200
|
+
scaleFunctionIndex,
|
|
13201
|
+
scaleParam: param,
|
|
13202
|
+
};
|
|
13203
|
+
}
|
|
13204
|
+
setFITSRangeMode(rangeMode) {
|
|
13205
|
+
if (rangeMode !== "robust" && rangeMode !== "hips" && rangeMode !== "tile") {
|
|
13206
|
+
throw new Error(`Unsupported FITS range mode: ${rangeMode}`);
|
|
13207
|
+
}
|
|
13208
|
+
this._fitsStretch = {
|
|
13209
|
+
...this._fitsStretch,
|
|
13210
|
+
rangeMode,
|
|
13211
|
+
};
|
|
13212
|
+
}
|
|
13213
|
+
observeFITSDataRange(fits) {
|
|
13214
|
+
if (fits.robustMin === null ||
|
|
13215
|
+
fits.robustMax === null ||
|
|
13216
|
+
!Number.isFinite(fits.robustMin) ||
|
|
13217
|
+
!Number.isFinite(fits.robustMax) ||
|
|
13218
|
+
fits.robustMax <= fits.robustMin) {
|
|
13219
|
+
return;
|
|
13220
|
+
}
|
|
13221
|
+
this._robustFITSRanges.push({
|
|
13222
|
+
min: fits.robustMin,
|
|
13223
|
+
max: fits.robustMax,
|
|
13224
|
+
});
|
|
13225
|
+
if (this._robustFITSRanges.length > MAX_ROBUST_TILE_RANGES) {
|
|
13226
|
+
this._robustFITSRanges.shift();
|
|
13227
|
+
}
|
|
13228
|
+
}
|
|
13229
|
+
getFITSDisplayRange(fits) {
|
|
13230
|
+
const hipsRange = this.getHiPSDataRange();
|
|
13231
|
+
if (this._fitsStretch.rangeMode === "hips" && hipsRange) {
|
|
13232
|
+
return hipsRange;
|
|
13233
|
+
}
|
|
13234
|
+
if (this._fitsStretch.rangeMode === "robust") {
|
|
13235
|
+
const robustRange = this.getRobustFITSRange() ?? this.getTileRobustRange(fits);
|
|
13236
|
+
if (robustRange) {
|
|
13237
|
+
return robustRange;
|
|
13238
|
+
}
|
|
13239
|
+
}
|
|
13240
|
+
const tileRange = this.getTilePhysicalRange(fits) ?? this.getTileHeaderRange(fits);
|
|
13241
|
+
if (tileRange) {
|
|
13242
|
+
return tileRange;
|
|
13243
|
+
}
|
|
13244
|
+
return hipsRange;
|
|
13245
|
+
}
|
|
13246
|
+
getHiPSDataRange() {
|
|
13247
|
+
const hipsRange = this.dataRange;
|
|
13248
|
+
if (hipsRange.min !== undefined &&
|
|
13249
|
+
hipsRange.max !== undefined &&
|
|
13250
|
+
Number.isFinite(hipsRange.min) &&
|
|
13251
|
+
Number.isFinite(hipsRange.max) &&
|
|
13252
|
+
hipsRange.max > hipsRange.min) {
|
|
13253
|
+
return {
|
|
13254
|
+
min: hipsRange.min,
|
|
13255
|
+
max: hipsRange.max,
|
|
13256
|
+
};
|
|
13257
|
+
}
|
|
13258
|
+
return null;
|
|
13259
|
+
}
|
|
13260
|
+
getRobustFITSRange() {
|
|
13261
|
+
if (this._robustFITSRanges.length === 0) {
|
|
13262
|
+
return null;
|
|
13263
|
+
}
|
|
13264
|
+
const lows = this._robustFITSRanges.map((range) => range.min).sort((a, b) => a - b);
|
|
13265
|
+
const highs = this._robustFITSRanges.map((range) => range.max).sort((a, b) => a - b);
|
|
13266
|
+
const min = lows[Math.floor((lows.length - 1) * 0.1)];
|
|
13267
|
+
const max = highs[Math.ceil((highs.length - 1) * 0.9)];
|
|
13268
|
+
if (!Number.isFinite(min) || !Number.isFinite(max) || max <= min) {
|
|
13269
|
+
return null;
|
|
13270
|
+
}
|
|
13271
|
+
return { min, max };
|
|
13272
|
+
}
|
|
13273
|
+
getTileRobustRange(fits) {
|
|
13274
|
+
if (fits &&
|
|
13275
|
+
fits.robustMin !== null &&
|
|
13276
|
+
fits.robustMax !== null &&
|
|
13277
|
+
Number.isFinite(fits.robustMin) &&
|
|
13278
|
+
Number.isFinite(fits.robustMax) &&
|
|
13279
|
+
fits.robustMax > fits.robustMin) {
|
|
13280
|
+
return {
|
|
13281
|
+
min: fits.robustMin,
|
|
13282
|
+
max: fits.robustMax,
|
|
13283
|
+
};
|
|
13284
|
+
}
|
|
13285
|
+
return null;
|
|
13286
|
+
}
|
|
13287
|
+
getTilePhysicalRange(fits) {
|
|
13288
|
+
if (fits &&
|
|
13289
|
+
fits.physicalMin !== null &&
|
|
13290
|
+
fits.physicalMax !== null &&
|
|
13291
|
+
Number.isFinite(fits.physicalMin) &&
|
|
13292
|
+
Number.isFinite(fits.physicalMax) &&
|
|
13293
|
+
fits.physicalMax > fits.physicalMin) {
|
|
13294
|
+
return {
|
|
13295
|
+
min: fits.physicalMin,
|
|
13296
|
+
max: fits.physicalMax,
|
|
13297
|
+
};
|
|
13298
|
+
}
|
|
13299
|
+
return null;
|
|
13300
|
+
}
|
|
13301
|
+
getTileHeaderRange(fits) {
|
|
13302
|
+
if (fits &&
|
|
13303
|
+
fits.dataMin !== null &&
|
|
13304
|
+
fits.dataMax !== null &&
|
|
13305
|
+
Number.isFinite(fits.dataMin) &&
|
|
13306
|
+
Number.isFinite(fits.dataMax) &&
|
|
13307
|
+
fits.dataMax > fits.dataMin) {
|
|
13308
|
+
return {
|
|
13309
|
+
min: fits.dataMin,
|
|
13310
|
+
max: fits.dataMax,
|
|
13311
|
+
};
|
|
13312
|
+
}
|
|
13313
|
+
return null;
|
|
13314
|
+
}
|
|
13139
13315
|
getCurrentHealpixOrder() {
|
|
13140
13316
|
return this._visibleorder;
|
|
13141
13317
|
}
|
|
@@ -13646,6 +13822,7 @@ class Tile {
|
|
|
13646
13822
|
async loadFits() {
|
|
13647
13823
|
try {
|
|
13648
13824
|
this._fitsTile = await (0, FitsTileLoader_js_1.loadFitsTile)(this._texurl);
|
|
13825
|
+
this._hips.observeFITSDataRange(this._fitsTile);
|
|
13649
13826
|
this._ready = true;
|
|
13650
13827
|
}
|
|
13651
13828
|
catch (error) {
|
|
@@ -13784,30 +13961,7 @@ class Tile {
|
|
|
13784
13961
|
gl.bufferData(gl.ARRAY_BUFFER, this.vertexPosition[qidx], gl.STATIC_DRAW);
|
|
13785
13962
|
}
|
|
13786
13963
|
getFitsDataRange() {
|
|
13787
|
-
|
|
13788
|
-
if (hipsRange.min !== undefined &&
|
|
13789
|
-
hipsRange.max !== undefined &&
|
|
13790
|
-
Number.isFinite(hipsRange.min) &&
|
|
13791
|
-
Number.isFinite(hipsRange.max) &&
|
|
13792
|
-
hipsRange.max > hipsRange.min) {
|
|
13793
|
-
return {
|
|
13794
|
-
min: hipsRange.min,
|
|
13795
|
-
max: hipsRange.max,
|
|
13796
|
-
};
|
|
13797
|
-
}
|
|
13798
|
-
const fits = this._fitsTile;
|
|
13799
|
-
if (fits &&
|
|
13800
|
-
fits.dataMin !== null &&
|
|
13801
|
-
fits.dataMax !== null &&
|
|
13802
|
-
Number.isFinite(fits.dataMin) &&
|
|
13803
|
-
Number.isFinite(fits.dataMax) &&
|
|
13804
|
-
fits.dataMax > fits.dataMin) {
|
|
13805
|
-
return {
|
|
13806
|
-
min: fits.dataMin,
|
|
13807
|
-
max: fits.dataMax,
|
|
13808
|
-
};
|
|
13809
|
-
}
|
|
13810
|
-
return null;
|
|
13964
|
+
return this._hips.getFITSDisplayRange(this._fitsTile);
|
|
13811
13965
|
}
|
|
13812
13966
|
get inView() {
|
|
13813
13967
|
return this._inView;
|
|
@@ -13911,7 +14065,7 @@ class Tile {
|
|
|
13911
14065
|
console.error(`[Tile] No display range available for FITS tile ${this._texurl}`);
|
|
13912
14066
|
return;
|
|
13913
14067
|
}
|
|
13914
|
-
hipsShaderProgram.setTextureDataMode(this._hipsShaderIndex, true, range.min, range.max);
|
|
14068
|
+
hipsShaderProgram.setTextureDataMode(this._hipsShaderIndex, true, range.min, range.max, this._hips.fitsStretch.scaleFunctionIndex, this._hips.fitsStretch.scaleParam);
|
|
13915
14069
|
}
|
|
13916
14070
|
else {
|
|
13917
14071
|
hipsShaderProgram.setTextureDataMode(this._hipsShaderIndex, false);
|
|
@@ -16756,6 +16910,8 @@ class HiPSShaderProgram {
|
|
|
16756
16910
|
textureMode: "uTextureMode",
|
|
16757
16911
|
dataMin: "uDataMin",
|
|
16758
16912
|
dataMax: "uDataMax",
|
|
16913
|
+
scaleFunction: "uScaleFunction",
|
|
16914
|
+
scaleParam: "uScaleParam",
|
|
16759
16915
|
};
|
|
16760
16916
|
this.gl_attributes = {
|
|
16761
16917
|
vertex_pos: "aVertexPosition",
|
|
@@ -16770,6 +16926,8 @@ class HiPSShaderProgram {
|
|
|
16770
16926
|
textureMode: new Array(8).fill(null),
|
|
16771
16927
|
dataMin: new Array(8).fill(null),
|
|
16772
16928
|
dataMax: new Array(8).fill(null),
|
|
16929
|
+
scaleFunction: new Array(8).fill(null),
|
|
16930
|
+
scaleParam: new Array(8).fill(null),
|
|
16773
16931
|
layerOpacity: null,
|
|
16774
16932
|
clorMapIdx: null,
|
|
16775
16933
|
vertexPositionAttribute: -1,
|
|
@@ -16903,7 +17061,7 @@ class HiPSShaderProgram {
|
|
|
16903
17061
|
}
|
|
16904
17062
|
gl.useProgram(this.shaderProgram);
|
|
16905
17063
|
}
|
|
16906
|
-
setTextureDataMode(samplerIndex, fits, min, max) {
|
|
17064
|
+
setTextureDataMode(samplerIndex, fits, min, max, scaleFunction = 0, scaleParam = 1) {
|
|
16907
17065
|
if (samplerIndex < 0 || samplerIndex >= 8) {
|
|
16908
17066
|
throw new Error(`Invalid HiPS sampler index: ${samplerIndex}`);
|
|
16909
17067
|
}
|
|
@@ -16924,12 +17082,20 @@ class HiPSShaderProgram {
|
|
|
16924
17082
|
}
|
|
16925
17083
|
const minLocation = this.locations.dataMin[samplerIndex];
|
|
16926
17084
|
const maxLocation = this.locations.dataMax[samplerIndex];
|
|
17085
|
+
const scaleFunctionLocation = this.locations.scaleFunction[samplerIndex];
|
|
17086
|
+
const scaleParamLocation = this.locations.scaleParam[samplerIndex];
|
|
16927
17087
|
if (minLocation) {
|
|
16928
17088
|
gl.uniform1f(minLocation, min);
|
|
16929
17089
|
}
|
|
16930
17090
|
if (maxLocation) {
|
|
16931
17091
|
gl.uniform1f(maxLocation, max);
|
|
16932
17092
|
}
|
|
17093
|
+
if (scaleFunctionLocation) {
|
|
17094
|
+
gl.uniform1i(scaleFunctionLocation, scaleFunction);
|
|
17095
|
+
}
|
|
17096
|
+
if (scaleParamLocation) {
|
|
17097
|
+
gl.uniform1f(scaleParamLocation, scaleParam);
|
|
17098
|
+
}
|
|
16933
17099
|
}
|
|
16934
17100
|
enableShaders(pMatrix, vMatrix, mMatrix, colorMapIdx) {
|
|
16935
17101
|
// const gl = global.gl
|
|
@@ -16945,6 +17111,8 @@ class HiPSShaderProgram {
|
|
|
16945
17111
|
this.locations.textureMode[i] = gl.getUniformLocation(this.shaderProgram, `uTextureMode${i}`);
|
|
16946
17112
|
this.locations.dataMin[i] = gl.getUniformLocation(this.shaderProgram, `uDataMin${i}`);
|
|
16947
17113
|
this.locations.dataMax[i] = gl.getUniformLocation(this.shaderProgram, `uDataMax${i}`);
|
|
17114
|
+
this.locations.scaleFunction[i] = gl.getUniformLocation(this.shaderProgram, `uScaleFunction${i}`);
|
|
17115
|
+
this.locations.scaleParam[i] = gl.getUniformLocation(this.shaderProgram, `uScaleParam${i}`);
|
|
16948
17116
|
if (this.locations.samplers[i]) {
|
|
16949
17117
|
gl.uniform1i(this.locations.samplers[i], i);
|
|
16950
17118
|
}
|
|
@@ -17281,7 +17449,7 @@ class ShaderManager {
|
|
|
17281
17449
|
}
|
|
17282
17450
|
static footprintVS() {
|
|
17283
17451
|
return `#version 300 es
|
|
17284
|
-
precision
|
|
17452
|
+
precision mediump float;
|
|
17285
17453
|
|
|
17286
17454
|
layout(location = 0) in vec4 aCatPosition;
|
|
17287
17455
|
|
|
@@ -17323,7 +17491,7 @@ class ShaderManager {
|
|
|
17323
17491
|
}
|
|
17324
17492
|
static hipsNativeFS() {
|
|
17325
17493
|
return `#version 300 es
|
|
17326
|
-
precision
|
|
17494
|
+
precision highp float;
|
|
17327
17495
|
|
|
17328
17496
|
in vec2 vTextureCoord;
|
|
17329
17497
|
|
|
@@ -17389,7 +17557,7 @@ class ShaderManager {
|
|
|
17389
17557
|
}
|
|
17390
17558
|
static hipsGrayscaleFS() {
|
|
17391
17559
|
return `#version 300 es
|
|
17392
|
-
precision
|
|
17560
|
+
precision highp float;
|
|
17393
17561
|
|
|
17394
17562
|
in vec2 vTextureCoord;
|
|
17395
17563
|
|
|
@@ -17463,7 +17631,7 @@ class ShaderManager {
|
|
|
17463
17631
|
}
|
|
17464
17632
|
static hipsColorMapFS() {
|
|
17465
17633
|
return `#version 300 es
|
|
17466
|
-
precision
|
|
17634
|
+
precision highp float;
|
|
17467
17635
|
|
|
17468
17636
|
uniform float uLayerOpacity;
|
|
17469
17637
|
|
|
@@ -17568,6 +17736,36 @@ class ShaderManager {
|
|
|
17568
17736
|
uniform int uTextureMode${index};
|
|
17569
17737
|
uniform float uDataMin${index};
|
|
17570
17738
|
uniform float uDataMax${index};
|
|
17739
|
+
uniform int uScaleFunction${index};
|
|
17740
|
+
uniform float uScaleParam${index};
|
|
17741
|
+
|
|
17742
|
+
float asinhCompat${index}(float value) {
|
|
17743
|
+
return log(value + sqrt(value * value + 1.0));
|
|
17744
|
+
}
|
|
17745
|
+
|
|
17746
|
+
float applyFitsScale${index}(float normalized) {
|
|
17747
|
+
float x = clamp(normalized, 0.0, 1.0);
|
|
17748
|
+
|
|
17749
|
+
if (uScaleFunction${index} == 1) {
|
|
17750
|
+
return sqrt(x);
|
|
17751
|
+
}
|
|
17752
|
+
|
|
17753
|
+
if (uScaleFunction${index} == 2) {
|
|
17754
|
+
float strength = max(uScaleParam${index}, 1.0);
|
|
17755
|
+
return log(1.0 + strength * x) / log(1.0 + strength);
|
|
17756
|
+
}
|
|
17757
|
+
|
|
17758
|
+
if (uScaleFunction${index} == 3) {
|
|
17759
|
+
float strength = max(uScaleParam${index}, 1.0);
|
|
17760
|
+
return asinhCompat${index}(strength * x) / asinhCompat${index}(strength);
|
|
17761
|
+
}
|
|
17762
|
+
|
|
17763
|
+
if (uScaleFunction${index} == 4) {
|
|
17764
|
+
return pow(x, max(uScaleParam${index}, 0.0001));
|
|
17765
|
+
}
|
|
17766
|
+
|
|
17767
|
+
return x;
|
|
17768
|
+
}
|
|
17571
17769
|
|
|
17572
17770
|
vec4 sampleHiPSTexture${index}(vec2 uv) {
|
|
17573
17771
|
vec4 color = texture(uSampler${index}, uv);
|
|
@@ -17591,10 +17789,12 @@ class ShaderManager {
|
|
|
17591
17789
|
1.0
|
|
17592
17790
|
);
|
|
17593
17791
|
|
|
17792
|
+
float scaled = applyFitsScale${index}(normalized);
|
|
17793
|
+
|
|
17594
17794
|
return vec4(
|
|
17595
|
-
|
|
17596
|
-
|
|
17597
|
-
|
|
17795
|
+
scaled,
|
|
17796
|
+
scaled,
|
|
17797
|
+
scaled,
|
|
17598
17798
|
1.0
|
|
17599
17799
|
);
|
|
17600
17800
|
}
|