mirror of
https://github.com/cds-astro/aladin-lite.git
synced 2025-12-26 04:42:40 -08:00
Compare commits
5 Commits
browse_hip
...
hover-effe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e0ed7393f7 | ||
|
|
0a64441774 | ||
|
|
eeb12f7b17 | ||
|
|
498556a448 | ||
|
|
f4d1ce3480 |
@@ -1,14 +1,5 @@
|
||||
# Changelogs
|
||||
|
||||
## 3.3.3
|
||||
|
||||
* [feat] UI: add HiPS basic filter that filters the `hipsList` given
|
||||
* [feat] New `hipsList` option parameter when instancing a new Aladin object.
|
||||
* [feat] Zoom smoothing using hermite cubic interpolation functions
|
||||
* [feat] shape option of Catalog and ProgressiveCat accepts a function returning a Footprint. This allow user to
|
||||
associate a footprint to a specific source
|
||||
* [feat] Hover color support by @pmatsson and @bmatthieu3 in <https://github.com/cds-astro/aladin-lite/pull/145>
|
||||
|
||||
## 3.3.2
|
||||
|
||||
* [fixed] do not allow to query the properties several times for an imageHiPS
|
||||
|
||||
@@ -25,7 +25,6 @@ Always prefer using the latest version. If you want the new features without min
|
||||
## API documentation
|
||||
|
||||
There is a new in progress API documentation at [this link](https://cds-astro.github.io/aladin-lite).
|
||||
Editable examples showing the API can also be found [here](https://aladin.cds.unistra.fr/AladinLite/doc/API/examples/).
|
||||
|
||||
## Embed it into your projects
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11 8C11 7.44772 11.4477 7 12 7C12.5523 7 13 7.44771 13 8V11H16C16.5523 11 17 11.4477 17 12C17 12.5523 16.5523 13 16 13H13V16C13 16.5523 12.5523 17 12 17C11.4477 17 11 16.5523 11 16V13H8C7.44772 13 7 12.5523 7 12C7 11.4477 7.44771 11 8 11H11V8Z" fill="#0F0F0F"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M23 4C23 2.34315 21.6569 1 20 1H4C2.34315 1 1 2.34315 1 4V20C1 21.6569 2.34315 23 4 23H20C21.6569 23 23 21.6569 23 20V4ZM21 4C21 3.44772 20.5523 3 20 3H4C3.44772 3 3 3.44772 3 4V20C3 20.5523 3.44772 21 4 21H20C20.5523 21 21 20.5523 21 20V4Z" fill="#0F0F0F"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 811 B |
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M15 15L21 21M21 15L15 21M10 21V14.6627C10 14.4182 10 14.2959 9.97237 14.1808C9.94787 14.0787 9.90747 13.9812 9.85264 13.8917C9.7908 13.7908 9.70432 13.7043 9.53137 13.5314L3.46863 7.46863C3.29568 7.29568 3.2092 7.2092 3.14736 7.10828C3.09253 7.01881 3.05213 6.92127 3.02763 6.81923C3 6.70414 3 6.58185 3 6.33726V4.6C3 4.03995 3 3.75992 3.10899 3.54601C3.20487 3.35785 3.35785 3.20487 3.54601 3.10899C3.75992 3 4.03995 3 4.6 3H19.4C19.9601 3 20.2401 3 20.454 3.10899C20.6422 3.20487 20.7951 3.35785 20.891 3.54601C21 3.75992 21 4.03995 21 4.6V6.33726C21 6.58185 21 6.70414 20.9724 6.81923C20.9479 6.92127 20.9075 7.01881 20.8526 7.10828C20.7908 7.2092 20.7043 7.29568 20.5314 7.46863L17 11" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1012 B |
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3 4.6C3 4.03995 3 3.75992 3.10899 3.54601C3.20487 3.35785 3.35785 3.20487 3.54601 3.10899C3.75992 3 4.03995 3 4.6 3H19.4C19.9601 3 20.2401 3 20.454 3.10899C20.6422 3.20487 20.7951 3.35785 20.891 3.54601C21 3.75992 21 4.03995 21 4.6V6.33726C21 6.58185 21 6.70414 20.9724 6.81923C20.9479 6.92127 20.9075 7.01881 20.8526 7.10828C20.7908 7.2092 20.7043 7.29568 20.5314 7.46863L14.4686 13.5314C14.2957 13.7043 14.2092 13.7908 14.1474 13.8917C14.0925 13.9812 14.0521 14.0787 14.0276 14.1808C14 14.2959 14 14.4182 14 14.6627V17L10 21V14.6627C10 14.4182 10 14.2959 9.97237 14.1808C9.94787 14.0787 9.90747 13.9812 9.85264 13.8917C9.7908 13.7908 9.70432 13.7043 9.53137 13.5314L3.46863 7.46863C3.29568 7.29568 3.2092 7.2092 3.14736 7.10828C3.09253 7.01881 3.05213 6.92127 3.02763 6.81923C3 6.70414 3 6.58185 3 6.33726V4.6Z" stroke="#000000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -8,8 +8,8 @@
|
||||
"dateModified": "2023-01-31",
|
||||
"issueTracker": "https://github.com/cds-astro/aladin-lite/issues",
|
||||
"name": "Aladin Lite",
|
||||
"version": "3.4.0-beta",
|
||||
"softwareVersion": "3.4.0-beta",
|
||||
"version": "3.3.2",
|
||||
"softwareVersion": "3.3.2",
|
||||
"description": "An astronomical HiPS visualizer in the browser.",
|
||||
"identifier": "10.5281/zenodo.7638833",
|
||||
"applicationCategory": "Astronomy, Visualization",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
import A from '../src/js/A.js';
|
||||
let aladin;
|
||||
A.init.then(() => {
|
||||
aladin = A.aladin('#aladin-lite-div', {survey: ["P/PanSTARRS/DR1/color-i-r-g"], showReticle: false, projection: "AIT", cooFrame: 'icrs', target: "stephan's quintet", fov: 1000, showGotoControl: false, showFrame: false, fullScreen: true, showLayersControl: true, showCooGrid: true, showCooGridControl: false});
|
||||
aladin = A.aladin('#aladin-lite-div', {survey: ["P/PanSTARRS/DR1/color-i-r-g"], showReticle: false, gridOptions: {opacity: 0.5, color: 'rgba(255, 0, 0)'}, projection: "AIT", cooFrame: 'icrs', target: "stephan's quintet", fov: 1000, showGotoControl: false, showFrame: false, fullScreen: true, showLayersControl: true, showCooGrid: true, showCooGridControl: false});
|
||||
|
||||
const chft = aladin.createImageSurvey('CFHT', "CFHT deep view of NGC7331 and Stephan's quintet u+g+r", "https://cds.unistra.fr/~derriere/PR_HiPS/2022_Duc/", null, null, {imgFormat: 'png'});
|
||||
const nircamJWST = aladin.createImageSurvey('Nircam', "Stephans Quintet NIRCam+MIRI", "http://alasky.cds.unistra.fr/JWST/CDS_P_JWST_Stephans-Quintet_NIRCam+MIRI/", null, null, {imgFormat: 'png', colormap: "viridis"});
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="module">
|
||||
import A from '../src/js/A.js';
|
||||
var aladin;
|
||||
let aladin;
|
||||
A.init.then(() => {
|
||||
aladin = A.aladin('#aladin-lite-div', {projection: 'MOL', cooFrame: 'galactic', fov: 360, fullScreen: true, showCooGrid: false, showReticle: false})
|
||||
aladin.gotoRaDec(79.9525321, -69.2742586)
|
||||
@@ -51,7 +52,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
aladin.setCooGrid({enabled: true, opacity: 0, color: {r: 51, g: 209, b: 255}})
|
||||
aladin.view.setGridConfig({opacity: 0, color: {r: 51/255, g: 209/255, b: 1}})
|
||||
aladin.view.setGridConfig({enabled: true})
|
||||
|
||||
async function s_1() {
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -96,7 +98,7 @@
|
||||
|
||||
async function showGrid() {
|
||||
for await(const it of interval(50, 40)) {
|
||||
aladin.setCooGrid({opacity: it / 40})
|
||||
aladin.view.setGridConfig({opacity: it / 40})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +155,7 @@
|
||||
|
||||
async function hideGrid() {
|
||||
for await(const it of interval(50, 40)) {
|
||||
aladin.setCooGrid({opacity: 1 - it / 40})
|
||||
aladin.view.setGridConfig({opacity: 1 - it / 40})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="aladin-lite-div" style="width: 1024px; height: 100%;"></div>
|
||||
<div id="aladin-lite-div" style="width: 1024px; height: 768px"></div>
|
||||
|
||||
<script type="module">
|
||||
import A from '../src/js/A.js';
|
||||
@@ -19,6 +19,7 @@
|
||||
target: '19 24 51.556 +45 16 44.36', // initial target
|
||||
cooFrame: 'equatorial', // set galactic frame
|
||||
showCooGrid: true, // set the grid
|
||||
fullScreen: true,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="aladin-lite-div" style="width: 1024px; height: 768px"></div>
|
||||
<script>let aladin;</script>
|
||||
<script type="module">
|
||||
import A from '../src/js/A.js';
|
||||
A.init.then(() => {
|
||||
// Start up Aladin Lite
|
||||
aladin = A.aladin('#aladin-lite-div', {
|
||||
target: "M31",
|
||||
fov: 89.78,
|
||||
showContextMenu: true,
|
||||
fullScreen: true,
|
||||
showSimbadPointerControl: true,
|
||||
showShareControl: true,
|
||||
showSettingsControl: true,
|
||||
showStackLayerControl: true,
|
||||
samp: true,
|
||||
});
|
||||
|
||||
aladin.addCatalog(A.catalogFromVizieR("VII/237/pgc", "M31", 3, {
|
||||
limit: 1000,
|
||||
//orderBy: 'nb_ref',
|
||||
onClick: 'showTable',
|
||||
color: 'yellow',
|
||||
hoverColor: 'blue',
|
||||
shape: (s) => {
|
||||
let coo = A.coo();
|
||||
coo.parse(s.data['RAJ2000'] + ' ' + s.data['DEJ2000'])
|
||||
|
||||
let a = (0.1 * Math.pow(10, +s.data.logD25)) / 60;
|
||||
let b = (1.0 / Math.pow(10, +s.data.logR25)) * a
|
||||
|
||||
return A.ellipse(coo.lon, coo.lat, a, b, +s.data.PA, {lineWidth: 3});
|
||||
}
|
||||
}));
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,115 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="aladin-lite-div" style="width: 1024px; height: 768px"></div>
|
||||
<script>let aladin;</script>
|
||||
<script type="module">
|
||||
import A from '../src/js/A.js';
|
||||
A.init.then(() => {
|
||||
// Start up Aladin Lite
|
||||
aladin = A.aladin('#aladin-lite-div', {
|
||||
target: "LMC",
|
||||
fov: 10,
|
||||
showContextMenu: true,
|
||||
fullScreen: true,
|
||||
showSimbadPointerControl: true,
|
||||
showShareControl: true,
|
||||
showSettingsControl: true,
|
||||
showStackLayerControl: true,
|
||||
samp: true,
|
||||
});
|
||||
|
||||
let pmraMean = null, pmdecMean = null;
|
||||
|
||||
const pmCat = A.catalogFromURL('./data/proper_motion.xml', {
|
||||
onClick: 'showTable',
|
||||
name: 'mean pm over HPX cells around LMC from GaiaDR2',
|
||||
hoverColor: 'yellow',
|
||||
selectionColor: 'white',
|
||||
// Footprint associated to sources
|
||||
shape: (s) => {
|
||||
|
||||
// Compute the mean of pm over the catalog sources
|
||||
if (!pmraMean || !pmdecMean) {
|
||||
pmraMean = 0, pmdecMean = 0;
|
||||
for (var s of pmCat.getSources()) {
|
||||
pmraMean += +s.data.pmra;
|
||||
pmdecMean += +s.data.pmdec;
|
||||
}
|
||||
|
||||
const numSources = pmCat.getSources().length;
|
||||
|
||||
pmraMean /= numSources
|
||||
pmdecMean /= numSources
|
||||
}
|
||||
|
||||
console.log("mean", pmraMean, pmdecMean)
|
||||
|
||||
let dra = +s.data.pmra - pmraMean;
|
||||
let ddec = +s.data.pmdec - pmdecMean;
|
||||
|
||||
// discard drawing a vector for big pm
|
||||
|
||||
|
||||
let totalPmSquared = s.data.pmra*s.data.pmra + s.data.pmdec*s.data.pmdec;
|
||||
if (totalPmSquared > 6) {
|
||||
return;
|
||||
}
|
||||
|
||||
let color = rainbowColorMap((totalPmSquared - 2.5) / 2)
|
||||
|
||||
return A.vector(
|
||||
s.ra,
|
||||
s.dec,
|
||||
s.ra + dra,
|
||||
s.dec + ddec,
|
||||
null,
|
||||
{lineWidth: 3, color}
|
||||
)
|
||||
}
|
||||
});
|
||||
aladin.addCatalog(pmCat);
|
||||
});
|
||||
|
||||
function rainbowColorMap(value) {
|
||||
// Ensure value is within range [0, 1]
|
||||
value = Math.max(0, Math.min(1, value));
|
||||
|
||||
// Convert value to hue
|
||||
var hue = (1 - value) * 240; // 240 is the maximum hue value for blue
|
||||
|
||||
// Convert HSV to RGB
|
||||
var chroma = 1;
|
||||
var x = chroma * (1 - Math.abs((hue / 60) % 2 - 1));
|
||||
var r1, g1, b1;
|
||||
|
||||
if (hue >= 0 && hue < 60) {
|
||||
[r1, g1, b1] = [chroma, x, 0];
|
||||
} else if (hue >= 60 && hue < 120) {
|
||||
[r1, g1, b1] = [x, chroma, 0];
|
||||
} else if (hue >= 120 && hue < 180) {
|
||||
[r1, g1, b1] = [0, chroma, x];
|
||||
} else if (hue >= 180 && hue < 240) {
|
||||
[r1, g1, b1] = [0, x, chroma];
|
||||
}
|
||||
|
||||
var m = 1 - chroma;
|
||||
var r = r1 + m;
|
||||
var g = g1 + m;
|
||||
var b = b1 + m;
|
||||
|
||||
// Convert RGB to HEX
|
||||
r = Math.round(r * 255);
|
||||
g = Math.round(g * 255);
|
||||
b = Math.round(b * 255);
|
||||
var colorHex = "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
|
||||
|
||||
return colorHex;
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,42 +1,42 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head> </head>
|
||||
<body>
|
||||
<div id="aladin-lite-div" style="width: 1024px; height: 768px"></div>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<script type="module">
|
||||
import A from "../src/js/A.js";
|
||||
let aladin;
|
||||
A.init.then(() => {
|
||||
aladin = A.aladin("#aladin-lite-div", {
|
||||
target: "12 25 41.512 +12 48 47.2",
|
||||
inertia: false,
|
||||
fov: 1,
|
||||
showContextMenu: true,
|
||||
});
|
||||
// define custom draw function
|
||||
<div id="aladin-lite-div" style="width: 1024px; height: 768px"></div>
|
||||
|
||||
var hips = A.catalogHiPS(
|
||||
"https://axel.u-strasbg.fr/HiPSCatService/Simbad",
|
||||
{
|
||||
onClick: "showTable",
|
||||
name: "Simbad",
|
||||
color: "cyan",
|
||||
hoverColor: "red",
|
||||
shape: (s) => {
|
||||
let a = +s.data.size_maj;
|
||||
let b = +s.data.size_min;
|
||||
<script type="module">
|
||||
import A from '../src/js/A.js';
|
||||
let aladin;
|
||||
A.init.then(() => {
|
||||
aladin = A.aladin('#aladin-lite-div', {target: 'LMC', fov: 55, showContextMenu: true});
|
||||
// define custom draw function
|
||||
var drawFunction = function(source, canvasCtx, viewParams) {
|
||||
canvasCtx.beginPath();
|
||||
canvasCtx.arc(source.x, source.y, source.data['coo_err_min'] * 5, 0, 2 * Math.PI, false);
|
||||
canvasCtx.closePath();
|
||||
canvasCtx.strokeStyle = '#c38';
|
||||
canvasCtx.lineWidth = 3;
|
||||
canvasCtx.globalAlpha = 0.7,
|
||||
canvasCtx.stroke();
|
||||
var fov = Math.max(viewParams['fov'][0], viewParams['fov'][1]);
|
||||
|
||||
let galaxy = ["Seyfert","Gin","StarburstG","LINER","AGN","Galaxy"].some((n) => s.data.main_type.indexOf(n) >= 0);
|
||||
if (!galaxy) return;
|
||||
// object name is displayed only if fov<10°
|
||||
if (fov>10) {
|
||||
return;
|
||||
}
|
||||
|
||||
let theta = +s.data.size_angle || 0.0;
|
||||
return A.ellipse(s.ra, s.dec, a / 60, b / 60, theta, { color: "cyan" });
|
||||
},
|
||||
}
|
||||
);
|
||||
aladin.addCatalog(hips);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
canvasCtx.globalAlpha = 0.9;
|
||||
canvasCtx.globalAlpha = 1;
|
||||
};
|
||||
|
||||
var hips = A.catalogHiPS('https://axel.u-strasbg.fr/HiPSCatService/Simbad', {onClick: 'showTable', name: 'Simbad', shape: drawFunction});
|
||||
aladin.addCatalog(hips);
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
|
||||
@@ -231,7 +231,7 @@
|
||||
|
||||
A.init.then(() => {
|
||||
var hipsDir="http://alasky.u-strasbg.fr/CDS_P_Coronelli";
|
||||
aladin = A.aladin("#aladin-lite-div", {showSimbadPointerControl: true, expandLayersControl: true, realFullscreen: true, fov: 100, allowFullZoomout: true, showReticle: false });
|
||||
aladin = A.aladin("#aladin-lite-div", {showSimbadPointerControl: true, realFullscreen: true, fov: 100, allowFullZoomout: true, showReticle: false });
|
||||
aladin.createImageSurvey('illenoroC', 'illenoroC', hipsDir, 'equatorial', 4, {imgFormat: 'jpg', longitudeReversed: false});
|
||||
aladin.createImageSurvey('Coronelli', 'Coronelli', hipsDir, 'equatorial', 4, {imgFormat: 'jpg', longitudeReversed: true});
|
||||
aladin.setImageSurvey('Coronelli');
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
}, // no optional params
|
||||
(ra, dec, fov, image) => {
|
||||
// ra, dec and fov are centered around the fits image
|
||||
console.log("jjj", image)
|
||||
image.setColormap("magma", {stretch: "asinh"});
|
||||
|
||||
aladin.gotoRaDec(ra, dec);
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
});
|
||||
|
||||
aladin.addCatalog(A.catalogFromSimbad('M 82', 0.1, {onClick: 'showTable'}));
|
||||
aladin.addCatalog(A.catalogFromNED('09 55 52.4 +69 40 47', 0.1, {onClick: 'showPopup', shape: 'plus'}));
|
||||
aladin.addCatalog(A.catalogFromNED('09 55 52.4 +69 40 47', 0.1, {onClick: 'showPopup', shape: 'plus'}));
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ A.init.then(() => {
|
||||
|
||||
aladin.addCatalog(A.catalogFromURL(vmc_cepheids, {onClick: 'showTable', sourceSize:14, color: '#fff080'}));
|
||||
|
||||
|
||||
|
||||
aladin.addCatalog(A.catalogFromURL(pessto, {onClick: 'showPopup', sourceSize:14, color: '#00f080'}));
|
||||
aladin.on('select', (objs) => {
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
import A from '../src/js/A.js';
|
||||
A.init.then(() => {
|
||||
var aladin = A.aladin('#aladin-lite-div', {target: '05 37 58 +08 17 35', fov: 12, backgroundColor: 'rgb(120, 0, 0)'});
|
||||
var cat = A.catalog({sourceSize: 20, onClick: 'showTable'});
|
||||
var cat = A.catalog({sourceSize: 20});
|
||||
aladin.addCatalog(cat);
|
||||
cat.addSources([A.source(83.784490, 9.934156, {name: 'Meissa'}), A.source(88.792939, 7.407064, {name: 'Betelgeuse'}), A.source(81.282764, 6.349703, {name: 'Bellatrix'})]);
|
||||
var msg;
|
||||
@@ -51,8 +51,6 @@
|
||||
}
|
||||
$('#infoDiv').html(msg);
|
||||
});
|
||||
|
||||
cat.sources[0].actionClicked();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
console.log("Object hovered stopped: ", object, "mouse coords xy: ", xyMouseCoords.x, xyMouseCoords.y);
|
||||
})
|
||||
|
||||
const cat = A.catalogFromVizieR('B/assocdata/obscore', 'M 1', 10, {onClick: 'showTable', hoverColor: 'purple', limit: 10000});
|
||||
const cat = A.catalogFromVizieR('B/assocdata/obscore', 'M 1', 100, {onClick: 'showTable', limit: 1000});
|
||||
aladin.addCatalog(cat);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -5,10 +5,11 @@
|
||||
<body>
|
||||
|
||||
<div id="aladin-lite-div" style="width: 1024px; height: 768px"></div>
|
||||
<script> let aladin;
|
||||
</script>
|
||||
|
||||
<script type="module">
|
||||
import A from '../src/js/A.js';
|
||||
var aladin;
|
||||
|
||||
A.init.then(() => {
|
||||
aladin = A.aladin(
|
||||
'#aladin-lite-div',
|
||||
@@ -21,10 +22,9 @@
|
||||
reticleColor: '#00ff00', // change reticle color
|
||||
reticleSize: 40, // change reticle size
|
||||
gridOptions: {color: 'pink'},
|
||||
showCooGrid: false, // set the grid
|
||||
showCooGrid: true, // set the grid
|
||||
fullScreen: true,
|
||||
inertia: false,
|
||||
showStatusBar: true,
|
||||
showStatusBar: false,
|
||||
showShareControl: true,
|
||||
showSettingsControl: true,
|
||||
showLayersControl: true,
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
fov: 360,
|
||||
target: '0 0',
|
||||
fullScreen: true,
|
||||
survey: ["azef", "jfjfj", "jfj", "P/allWISE/color"],
|
||||
survey: ["fsdfsd", "jfjfj", "jfj", "P/allWISE/color"],
|
||||
showCooGrid: true,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
aladin.addCatalog(catalog)
|
||||
});
|
||||
|
||||
aladin.addCatalog(A.catalogFromVizieR("B/assocdata/obscore", "0 +0", 20, {onClick: 'showTable', hoverColor: 'yellow', limit: 1000}))
|
||||
aladin.addCatalog(A.catalogFromVizieR("B/assocdata/obscore", "0 +0", 20, {limit: 1000, hoverColor: 'cyan'}))
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="aladin-lite-div" style="width: 500px; height: 400px"></div>
|
||||
|
||||
<script type="module">
|
||||
import A from '../src/js/A.js';
|
||||
|
||||
let aladin;
|
||||
A.init.then(() => {
|
||||
aladin = A.aladin('#aladin-lite-div', {
|
||||
fullScreen: true,
|
||||
target: "ngc 1436",
|
||||
fov: 2,
|
||||
projection: 'TAN',
|
||||
showContextMenu: true,
|
||||
showSimbadPointerControl: true,
|
||||
expandLayersControl: true,
|
||||
hipsList: [
|
||||
// High energy (Gamma and X-rays)
|
||||
'CDS/P/HGPS/Flux',
|
||||
'CDS/P/Fermi/5',
|
||||
'CDS/P/Fermi/4',
|
||||
'CDS/P/Fermi/3',
|
||||
'ov-gso/P/Fermi/Band2',
|
||||
'ov-gso/P/BAT/150-195keV',
|
||||
'ov-gso/P/BAT/35-50keV',
|
||||
'ov-gso/P/BAT/14-20keV',
|
||||
'erosita/dr1/rate/023',
|
||||
'erosita/dr1/rate/024',
|
||||
// Uv/Optical/Infrared
|
||||
'CDS/P/GALEXGR6_7/FUV',
|
||||
'CDS/P/GALEXGR6_7/NUV',
|
||||
'CDS/P/DSS2/color',
|
||||
'CDS/P/PanSTARRS/DR1/g',
|
||||
'CDS/P/PanSTARRS/DR1/r',
|
||||
'CDS/P/Finkbeiner',
|
||||
'CDS/P/PanSTARRS/DR1/i',
|
||||
'CDS/P/PanSTARRS/DR1/color-i-r-g',
|
||||
'CDS/P/PanSTARRS/DR1/z',
|
||||
'CDS/P/PanSTARRS/DR1/y',
|
||||
'CDS/P/DES-DR2/ColorIRG',
|
||||
'CDS/P/2MASS/color',
|
||||
'ov-gso/P/GLIMPSE/irac1',
|
||||
'ov-gso/P/GLIMPSE/irac2',
|
||||
'CDS/P/unWISE/color-W2-W1W2-W1',
|
||||
'ov-gso/P/GLIMPSE/irac3',
|
||||
'ov-gso/P/GLIMPSE/irac4',
|
||||
'CDS/P/IRIS/color',
|
||||
'ESAVO/P/AKARI/N60',
|
||||
'ESAVO/P/AKARI/WideL',
|
||||
'ESAVO/P/HERSCHEL/SPIRE-250',
|
||||
'ESAVO/P/HERSCHEL/SPIRE-350',
|
||||
'ESAVO/P/HERSCHEL/SPIRE-500',
|
||||
// sub-mm/mm/radio
|
||||
'CDS/P/PLANCK/R3/HFI/color',
|
||||
'CDS/P/ACT_Planck/DR5/f220',
|
||||
'CDS/P/CO',
|
||||
'CDS/P/PLANCK/R3/HFI100',
|
||||
'CDS/P/PLANCK/R3/LFI30',
|
||||
'CDS/P/NVSS',
|
||||
'CSIRO/P/RACS/mid/I',
|
||||
'ov-gso/P/CGPS/VGPS',
|
||||
'CDS/C/HI4PI/HI',
|
||||
'CDS/P/MeerKAT/Galactic-Centre-1284MHz-StokesI',
|
||||
'CSIRO/P/RACS/low/I',
|
||||
'astron.nl/P/tgssadr',
|
||||
'ov-gso/P/GLEAM/170-231',
|
||||
'ov-gso/P/GLEAM/139-170',
|
||||
'astron.nl/P/lotss_dr2_high',
|
||||
'ov-gso/P/GLEAM/103-134',
|
||||
'ov-gso/P/GLEAM/072-103'
|
||||
]
|
||||
});
|
||||
|
||||
aladin.addCatalog(
|
||||
A.catalogFromSKAORucio("ngc 1436", 15, {
|
||||
onClick: 'showTable',
|
||||
hoverColor: "yellow",
|
||||
})
|
||||
);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,32 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<!--<link rel="stylesheet" href="./layers.css" />-->
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="aladin-lite-div" style="width: 1024px; height: 768px"></div>
|
||||
|
||||
<script type="module">
|
||||
import A from '../src/js/A.js';
|
||||
var aladin;
|
||||
A.init.then(() => {
|
||||
aladin = A.aladin('#aladin-lite-div', {fullScreen: true, cooFrame: "ICRSd", showSimbadPointerControl: true, showShareControl: true, showShareControl: true, survey: 'https://alasky.cds.unistra.fr/DSS/DSSColor/', fov: 180, showContextMenu: true});
|
||||
// manage URL parameters
|
||||
let survey1 = aladin.getBaseImageLayer();
|
||||
survey1.setColormap('magma', {stretch: 'linear'});
|
||||
|
||||
let survey2 = aladin.newImageSurvey("CSIRO/P/RACS/low/I");
|
||||
aladin.setImageLayer(survey2)
|
||||
survey2.setColormap('rdbu', {stretch: 'linear'});
|
||||
|
||||
let survey3 = aladin.newImageSurvey("CSIRO/P/RACS/mid/I");
|
||||
aladin.setImageLayer(survey3)
|
||||
survey3.setColormap('cubehelix', {stretch: 'asinh'});
|
||||
|
||||
aladin.setImageLayer(survey2);
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,95 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="aladin-lite-div" style="width: 500px; height: 400px"></div>
|
||||
|
||||
<script type="module">
|
||||
import A from '../src/js/A.js';
|
||||
let aladin;
|
||||
A.init.then(() => {
|
||||
aladin = A.aladin('#aladin-lite-div', {
|
||||
fullScreen: true,
|
||||
target: "centaurus A",
|
||||
fov: 10,
|
||||
projection: 'TAN',
|
||||
showContextMenu: true,
|
||||
showSimbadPointerControl: true,
|
||||
expandLayersControl: true,
|
||||
hipsList: [
|
||||
// High energy (Gamma and X-rays)
|
||||
'CDS/P/HGPS/Flux',
|
||||
'CDS/P/Fermi/5',
|
||||
'CDS/P/Fermi/4',
|
||||
'CDS/P/Fermi/3',
|
||||
'ov-gso/P/Fermi/Band2',
|
||||
'ov-gso/P/BAT/150-195keV',
|
||||
'ov-gso/P/BAT/35-50keV',
|
||||
'ov-gso/P/BAT/14-20keV',
|
||||
'erosita/dr1/rate/023',
|
||||
'erosita/dr1/rate/024',
|
||||
// Uv/Optical/Infrared
|
||||
'CDS/P/GALEXGR6_7/FUV',
|
||||
'CDS/P/GALEXGR6_7/NUV',
|
||||
'CDS/P/DSS2/color',
|
||||
'CDS/P/PanSTARRS/DR1/g',
|
||||
'CDS/P/PanSTARRS/DR1/r',
|
||||
'CDS/P/Finkbeiner',
|
||||
'CDS/P/PanSTARRS/DR1/i',
|
||||
'CDS/P/PanSTARRS/DR1/color-i-r-g',
|
||||
'CDS/P/PanSTARRS/DR1/z',
|
||||
'CDS/P/PanSTARRS/DR1/y',
|
||||
'CDS/P/DES-DR2/ColorIRG',
|
||||
'CDS/P/2MASS/color',
|
||||
'ov-gso/P/GLIMPSE/irac1',
|
||||
'ov-gso/P/GLIMPSE/irac2',
|
||||
'CDS/P/unWISE/color-W2-W1W2-W1',
|
||||
'ov-gso/P/GLIMPSE/irac3',
|
||||
'ov-gso/P/GLIMPSE/irac4',
|
||||
'CDS/P/IRIS/color',
|
||||
'ESAVO/P/AKARI/N60',
|
||||
'ESAVO/P/AKARI/WideL',
|
||||
'ESAVO/P/HERSCHEL/SPIRE-250',
|
||||
'ESAVO/P/HERSCHEL/SPIRE-350',
|
||||
'ESAVO/P/HERSCHEL/SPIRE-500',
|
||||
// sub-mm/mm/radio
|
||||
'CDS/P/PLANCK/R3/HFI/color',
|
||||
'CDS/P/ACT_Planck/DR5/f220',
|
||||
'CDS/P/CO',
|
||||
'CDS/P/PLANCK/R3/HFI100',
|
||||
'CDS/P/PLANCK/R3/LFI30',
|
||||
'CDS/P/NVSS',
|
||||
'CSIRO/P/RACS/mid/I',
|
||||
'ov-gso/P/CGPS/VGPS',
|
||||
'CDS/C/HI4PI/HI',
|
||||
'CDS/P/MeerKAT/Galactic-Centre-1284MHz-StokesI',
|
||||
'CSIRO/P/RACS/low/I',
|
||||
'astron.nl/P/tgssadr',
|
||||
'ov-gso/P/GLEAM/170-231',
|
||||
'ov-gso/P/GLEAM/139-170',
|
||||
'astron.nl/P/lotss_dr2_high',
|
||||
'ov-gso/P/GLEAM/103-134',
|
||||
'ov-gso/P/GLEAM/072-103'
|
||||
]
|
||||
});
|
||||
|
||||
const gleam072_103 = aladin.newImageSurvey('ov-gso/P/GLEAM/072-103', {imgFormat: 'fits', colormap: "red", stretch: "asinh"});
|
||||
const gleam103_134 = aladin.newImageSurvey('ov-gso/P/GLEAM/103-134', {imgFormat: 'fits', colormap: "green", additive: true, stretch: "asinh"});
|
||||
const gleam170_231 = aladin.newImageSurvey('ov-gso/P/GLEAM/170-231', {imgFormat: 'fits', colormap: "blue", additive: true, stretch: "asinh"});
|
||||
|
||||
//const HSCBlueSurvey = aladin.newImageSurvey('CDS/P/HSC/DR2/deep/z', {imgFormat: 'fits', colormap: "blue", minCut: -0.01218, maxCut: 2.27397, additive: true, stretch: "asinh"});
|
||||
|
||||
aladin.setBaseImageLayer(gleam072_103);
|
||||
aladin.setOverlayImageLayer(gleam103_134, 'gl2');
|
||||
aladin.setOverlayImageLayer(gleam170_231, 'gl3');
|
||||
|
||||
//aladin.setOverlayImageLayer('CDS/P/HSC/DR2/deep/z', 'hsc blue layer');
|
||||
|
||||
//const c1 = A.catalogFromURL('./data/eso.xml', {onClick: 'showTable'});
|
||||
//aladin.addCatalog(c1);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,88 +1,33 @@
|
||||
<!DOCTYPE html>
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head> </head>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<body>
|
||||
<div id="aladin-lite-div" style="width: 500px; height: 400px"></div>
|
||||
<div id="aladin-lite-div" style="width: 500px; height: 400px"></div>
|
||||
|
||||
<script type="module">
|
||||
import A from "../src/js/A.js";
|
||||
let aladin;
|
||||
A.init.then(() => {
|
||||
aladin = A.aladin("#aladin-lite-div", {
|
||||
fullScreen: true,
|
||||
target: "m51",
|
||||
fov: 15,
|
||||
projection: "AIT",
|
||||
showContextMenu: true,
|
||||
showShareControl: true,
|
||||
hipsList: [
|
||||
// High energy (Gamma and X-rays)
|
||||
"CDS/P/HGPS/Flux",
|
||||
"CDS/P/Fermi/5",
|
||||
"CDS/P/Fermi/4",
|
||||
"CDS/P/Fermi/3",
|
||||
"ov-gso/P/Fermi/Band2",
|
||||
"ov-gso/P/BAT/150-195keV",
|
||||
"ov-gso/P/BAT/35-50keV",
|
||||
"ov-gso/P/BAT/14-20keV",
|
||||
"erosita/dr1/rate/023",
|
||||
"erosita/dr1/rate/024",
|
||||
// Uv/Optical/Infrared
|
||||
"CDS/P/GALEXGR6_7/FUV",
|
||||
"CDS/P/GALEXGR6_7/NUV",
|
||||
"CDS/P/DSS2/color",
|
||||
"CDS/P/PanSTARRS/DR1/g",
|
||||
"CDS/P/PanSTARRS/DR1/r",
|
||||
"CDS/P/Finkbeiner",
|
||||
"CDS/P/PanSTARRS/DR1/i",
|
||||
"CDS/P/PanSTARRS/DR1/color-i-r-g",
|
||||
"CDS/P/PanSTARRS/DR1/z",
|
||||
"CDS/P/PanSTARRS/DR1/y",
|
||||
"CDS/P/DES-DR2/ColorIRG",
|
||||
"CDS/P/2MASS/color",
|
||||
"ov-gso/P/GLIMPSE/irac1",
|
||||
"ov-gso/P/GLIMPSE/irac2",
|
||||
"CDS/P/unWISE/color-W2-W1W2-W1",
|
||||
"ov-gso/P/GLIMPSE/irac3",
|
||||
"ov-gso/P/GLIMPSE/irac4",
|
||||
"CDS/P/IRIS/color",
|
||||
"ESAVO/P/AKARI/N60",
|
||||
"ESAVO/P/AKARI/WideL",
|
||||
"ESAVO/P/HERSCHEL/SPIRE-250",
|
||||
"ESAVO/P/HERSCHEL/SPIRE-350",
|
||||
"ESAVO/P/HERSCHEL/SPIRE-500",
|
||||
// sub-mm/mm/radio
|
||||
"CDS/P/PLANCK/R3/HFI/color",
|
||||
"CDS/P/ACT_Planck/DR5/f220",
|
||||
"CDS/P/CO",
|
||||
"CDS/P/PLANCK/R3/HFI100",
|
||||
"CDS/P/PLANCK/R3/LFI30",
|
||||
"CDS/P/NVSS",
|
||||
"CSIRO/P/RACS/mid/I",
|
||||
"ov-gso/P/CGPS/VGPS",
|
||||
"CDS/C/HI4PI/HI",
|
||||
"CDS/P/MeerKAT/Galactic-Centre-1284MHz-StokesI",
|
||||
"CSIRO/P/RACS/low/I",
|
||||
"astron.nl/P/tgssadr",
|
||||
"ov-gso/P/GLEAM/170-231",
|
||||
"ov-gso/P/GLEAM/139-170",
|
||||
"astron.nl/P/lotss_dr2_high",
|
||||
"ov-gso/P/GLEAM/103-134",
|
||||
"ov-gso/P/GLEAM/072-103",
|
||||
],
|
||||
});
|
||||
<script type="module">
|
||||
import A from '../src/js/A.js';
|
||||
let aladin;
|
||||
A.init.then(() => {
|
||||
aladin = A.aladin('#aladin-lite-div', {fullScreen: true, target: "Abell 194", fov: 180, projection: 'AIT', showContextMenu: true});
|
||||
aladin.setImageSurvey('astron.nl/P/lotss_dr2_high')
|
||||
A.catalogFromSKAORucio("Abell 194", 90, {onClick: 'showTable'}, (cat) => {
|
||||
aladin.addCatalog(cat);
|
||||
});
|
||||
|
||||
aladin.setImageLayer(A.imageHiPS("P/DSS2/color"));
|
||||
/*A.catalogFromURL('https://aladin.cds.unistra.fr/AladinLite/ska/obscoreRucio.xml', {onClick: 'showTable'}, (cat) => {
|
||||
aladin.addCatalog(cat);
|
||||
});
|
||||
|
||||
aladin.addCatalog(
|
||||
A.catalogFromSKAORucio("m51", 15, {
|
||||
onClick: 'showTable',
|
||||
hoverColor: "yellow",
|
||||
})
|
||||
);
|
||||
//aladin.addCatalog(A.catalogFromURL('https://aladin.cds.unistra.fr/ObsCoreRucioScs.xml', {onClick: 'showTable'}));
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
A.catalogFromURL('data/votable/CDS-B-jcmt-obscore.xml', {onClick: 'showTable'}, (cat) => {
|
||||
aladin.addCatalog(cat);
|
||||
});*/
|
||||
A.catalogFromURL('https://aladin.cds.unistra.fr/ObsCoreRucioScs.xml', {onClick: 'showTable'}, (cat) => {
|
||||
aladin.addCatalog(cat);
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -8,13 +8,15 @@
|
||||
|
||||
<script type="module">
|
||||
import A from '../src/js/A.js';
|
||||
import {AladinUtils} from '../src/js/AladinUtils.js';
|
||||
|
||||
A.init.then(() => {
|
||||
let vertices = A.Utils.HEALPix.vertices(Math.pow(2, 3), BigInt(276))
|
||||
//let lonlat = A.Utils.HEALPix.pix2ang(8, 0n)
|
||||
//let ipix = A.Utils.HEALPix.ang2pix(8, 0.1, 0.4)
|
||||
//console.log("vertices", vertices, lonlat, ipix)
|
||||
console.log(vertices)
|
||||
let vertices = AladinUtils.HEALPix.vertices(8, 0n)
|
||||
|
||||
let lonlat = AladinUtils.HEALPix.pix2ang(8, 0n)
|
||||
|
||||
let ipix = AladinUtils.HEALPix.ang2pix(8, 0.1, 0.4)
|
||||
console.log(vertices, lonlat, ipix)
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
@@ -14,16 +14,17 @@
|
||||
aladin = A.aladin('#aladin-lite-div', {target: "30 0", fov: 360, fullScreen: true, cooFrame: 'galactic', showCooGridControl: true, showSimbadPointerControl: true, showCooGrid: true});
|
||||
aladin.setProjection('AIT');
|
||||
|
||||
|
||||
aladin.on("zoomChanged", () => {
|
||||
console.log("zoomChanged")
|
||||
})
|
||||
aladin.on("positionChanged", ({ra, dec}) => {
|
||||
console.log('call to aladin', aladin.pix2world(300, 300))
|
||||
console.log('positionChanged in icrs', ra, dec)
|
||||
aladin.on("positionChanged", ({ra, dec, dragging}) => {
|
||||
console.log("positionChanged", ra, dec)
|
||||
})
|
||||
|
||||
aladin.gotoRaDec(0, 20);
|
||||
|
||||
|
||||
aladin.on('rightClickMove', (x, y) => {
|
||||
console.log("right click move", x, y)
|
||||
})
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
}
|
||||
if (searchParams.has('showCooGrid')) {
|
||||
const b = searchParams.get('showCooGrid') === 'true';
|
||||
aladin.setCooGrid({
|
||||
aladin.view.setGridConfig({
|
||||
enabled: b
|
||||
});
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
}
|
||||
if (searchParams.has('showCooGrid')) {
|
||||
const b = searchParams.get('showCooGrid') === 'true';
|
||||
aladin.setCooGrid({
|
||||
aladin.view.setGridConfig({
|
||||
enabled: b
|
||||
});
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
}
|
||||
if (searchParams.has('showCooGrid')) {
|
||||
const b = searchParams.get('showCooGrid') === 'true';
|
||||
aladin.setCooGrid({
|
||||
aladin.view.setGridConfig({
|
||||
enabled: b
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"homepage": "https://aladin.u-strasbg.fr/",
|
||||
"name": "aladin-lite",
|
||||
"type": "module",
|
||||
"version": "3.4.1-beta",
|
||||
"version": "3.3.2",
|
||||
"description": "An astronomical HiPS visualizer in the browser",
|
||||
"author": "Thomas Boch and Matthieu Baumann",
|
||||
"license": "GPL-3",
|
||||
|
||||
@@ -3,7 +3,7 @@ name = "aladin-lite"
|
||||
description = "Aladin Lite v3 introduces a new graphical engine written in Rust with the use of WebGL"
|
||||
license = "BSD-3-Clause"
|
||||
repository = "https://github.com/cds-astro/aladin-lite"
|
||||
version = "3.4.0-beta"
|
||||
version = "3.3.2"
|
||||
authors = [ "baumannmatthieu0@gmail.com", "matthieu.baumann@astro.unistra.fr",]
|
||||
edition = "2018"
|
||||
|
||||
|
||||
@@ -17,34 +17,45 @@ pub enum Data<'a> {
|
||||
I32(Cow<'a, [i32]>),
|
||||
F32(Cow<'a, [f32]>),
|
||||
}
|
||||
use fitsrs::{fits::Fits as FitsData, hdu::data::InMemData};
|
||||
use std::io::Cursor;
|
||||
use fitsrs::{
|
||||
hdu::data::InMemData,
|
||||
fits::Fits as FitsData,
|
||||
};
|
||||
|
||||
impl<'a> Fits<'a> {
|
||||
pub fn from_byte_slice(bytes_reader: &'a mut Cursor<&[u8]>) -> Result<Self, JsValue> {
|
||||
let FitsData { hdu } = FitsData::from_reader(bytes_reader)
|
||||
.map_err(|_| JsValue::from_str(&"Parsing fits error"))?;
|
||||
.map_err(|_| {
|
||||
JsValue::from_str(&"Parsing fits error")
|
||||
})?;
|
||||
|
||||
let header = hdu.get_header();
|
||||
let xtension = header.get_xtension();
|
||||
let width = xtension
|
||||
.get_naxisn(1)
|
||||
let width = xtension.get_naxisn(1)
|
||||
.ok_or_else(|| JsValue::from_str("NAXIS1 not found in the fits"))?;
|
||||
|
||||
let height = xtension
|
||||
.get_naxisn(2)
|
||||
let height = xtension.get_naxisn(2)
|
||||
.ok_or_else(|| JsValue::from_str("NAXIS2 not found in the fits"))?;
|
||||
|
||||
|
||||
let data = hdu.get_data();
|
||||
let data = match *data {
|
||||
InMemData::U8(slice) => Data::U8(Cow::Borrowed(slice)),
|
||||
InMemData::I16(slice) => Data::I16(Cow::Borrowed(slice)),
|
||||
InMemData::I32(slice) => Data::I32(Cow::Borrowed(slice)),
|
||||
InMemData::U8(slice) => {
|
||||
Data::U8(Cow::Borrowed(slice))
|
||||
},
|
||||
InMemData::I16(slice) => {
|
||||
Data::I16(Cow::Borrowed(slice))
|
||||
},
|
||||
InMemData::I32(slice) => {
|
||||
Data::I32(Cow::Borrowed(slice))
|
||||
},
|
||||
InMemData::I64(slice) => {
|
||||
let data = slice.iter().map(|v| *v as i32).collect();
|
||||
Data::I32(Cow::Owned(data))
|
||||
}
|
||||
InMemData::F32(slice) => Data::F32(Cow::Borrowed(slice)),
|
||||
},
|
||||
InMemData::F32(slice) => {
|
||||
Data::F32(Cow::Borrowed(slice))
|
||||
},
|
||||
InMemData::F64(slice) => {
|
||||
let data = slice.iter().map(|v| *v as f32).collect();
|
||||
Data::F32(Cow::Owned(data))
|
||||
@@ -55,8 +66,8 @@ impl<'a> Fits<'a> {
|
||||
// Tile size
|
||||
size: Vector2::new(*width as i32, *height as i32),
|
||||
|
||||
// Allocation info of the layout
|
||||
data,
|
||||
// Allocation info of the layout
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
@@ -110,14 +121,14 @@ impl<'a> Fits<'a> {
|
||||
// Tile size
|
||||
size: Vector2::new(*width as i32, *height as i32),
|
||||
|
||||
// Allocation info of the layout
|
||||
// Allocation info of the layout
|
||||
data
|
||||
})
|
||||
}
|
||||
}*/
|
||||
|
||||
use crate::image::Image;
|
||||
use crate::Texture2DArray;
|
||||
use crate::image::Image;
|
||||
impl Image for Fits<'_> {
|
||||
fn tex_sub_image_3d(
|
||||
&self,
|
||||
@@ -127,7 +138,7 @@ impl Image for Fits<'_> {
|
||||
offset: &Vector3<i32>,
|
||||
) -> Result<(), JsValue> {
|
||||
match &self.data {
|
||||
Data::U8(data) => {
|
||||
Data::U8(data) => {
|
||||
let view = unsafe { R8UI::view(&data) };
|
||||
textures[offset.z as usize]
|
||||
.bind()
|
||||
@@ -139,7 +150,7 @@ impl Image for Fits<'_> {
|
||||
Some(view.as_ref()),
|
||||
);
|
||||
}
|
||||
Data::I16(data) => {
|
||||
Data::I16(data) => {
|
||||
let view = unsafe { R16I::view(&data) };
|
||||
textures[offset.z as usize]
|
||||
.bind()
|
||||
@@ -151,7 +162,7 @@ impl Image for Fits<'_> {
|
||||
Some(view.as_ref()),
|
||||
);
|
||||
}
|
||||
Data::I32(data) => {
|
||||
Data::I32(data) => {
|
||||
let view = unsafe { R32I::view(&data) };
|
||||
textures[offset.z as usize]
|
||||
.bind()
|
||||
@@ -163,7 +174,7 @@ impl Image for Fits<'_> {
|
||||
Some(view.as_ref()),
|
||||
);
|
||||
}
|
||||
Data::F32(data) => {
|
||||
Data::F32(data) => {
|
||||
let view = unsafe { R32F::view(&data) };
|
||||
textures[offset.z as usize]
|
||||
.bind()
|
||||
@@ -181,8 +192,8 @@ impl Image for Fits<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
use crate::image::format::ImageFormat;
|
||||
use wasm_bindgen::JsValue;
|
||||
use crate::image::format::ImageFormat;
|
||||
|
||||
pub trait FitsImageFormat: ImageFormat {
|
||||
const BITPIX: i8;
|
||||
@@ -194,7 +205,7 @@ impl FitsImageFormat for R32F {
|
||||
}
|
||||
|
||||
#[cfg(feature = "webgl2")]
|
||||
use crate::image::{R16I, R32I, R64F, R8UI};
|
||||
use crate::image::{R16I, R32I, R8UI, R64F};
|
||||
#[cfg(feature = "webgl2")]
|
||||
impl FitsImageFormat for R64F {
|
||||
const BITPIX: i8 = -64;
|
||||
|
||||
@@ -44,9 +44,7 @@ impl ImageFormat for RGB8U {
|
||||
|
||||
fn decode(raw_bytes: &[u8]) -> Result<Bytes<'_>, &'static str> {
|
||||
let mut decoder = jpeg::Decoder::new(raw_bytes);
|
||||
let bytes = decoder
|
||||
.decode()
|
||||
.map_err(|_| "Cannot decoder jpeg. This image may not be compressed.")?;
|
||||
let bytes = decoder.decode().map_err(|_| "Cannot decoder jpeg. This image may not be compressed.")?;
|
||||
|
||||
Ok(Bytes::Owned(bytes))
|
||||
}
|
||||
@@ -72,9 +70,7 @@ impl ImageFormat for RGBA8U {
|
||||
|
||||
fn decode(raw_bytes: &[u8]) -> Result<Bytes<'_>, &'static str> {
|
||||
let mut decoder = jpeg::Decoder::new(raw_bytes);
|
||||
let bytes = decoder
|
||||
.decode()
|
||||
.map_err(|_| "Cannot decoder png. This image may not be compressed.")?;
|
||||
let bytes = decoder.decode().map_err(|_| "Cannot decoder png. This image may not be compressed.")?;
|
||||
|
||||
Ok(Bytes::Owned(bytes))
|
||||
}
|
||||
@@ -97,9 +93,7 @@ impl ImageFormat for RGBA8U {
|
||||
|
||||
fn decode(raw_bytes: &[u8]) -> Result<Bytes<'_>, &'static str> {
|
||||
let mut decoder = jpeg::Decoder::new(raw_bytes);
|
||||
let bytes = decoder
|
||||
.decode()
|
||||
.map_err(|_| "Cannot decoder png. This image may not be compressed.")?;
|
||||
let bytes = decoder.decode().map_err(|_| "Cannot decoder png. This image may not be compressed.")?;
|
||||
|
||||
Ok(Bytes::Owned(bytes))
|
||||
}
|
||||
@@ -194,6 +188,7 @@ impl ImageFormat for R32F {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(feature = "webgl2")]
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct R64F;
|
||||
@@ -315,8 +310,6 @@ pub enum ChannelType {
|
||||
R32I,
|
||||
}
|
||||
|
||||
pub const NUM_CHANNELS: usize = 9;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
|
||||
pub struct ImageFormatType {
|
||||
pub ext: ImageExt,
|
||||
@@ -334,11 +327,8 @@ impl ImageFormatType {
|
||||
|
||||
pub fn is_colored(&self) -> bool {
|
||||
match self.channel {
|
||||
ChannelType::RGBA32F
|
||||
| ChannelType::RGB32F
|
||||
| ChannelType::RGBA8U
|
||||
| ChannelType::RGB8U => true,
|
||||
_ => false,
|
||||
ChannelType::RGBA32F | ChannelType::RGB32F | ChannelType::RGBA8U | ChannelType::RGB8U => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,12 +27,12 @@ impl Texture2DArray {
|
||||
tex_params: &'static [(u32, u32)],
|
||||
) -> Result<Texture2DArray, JsValue> {
|
||||
let textures: Result<Vec<_>, _> = (0..num_slices)
|
||||
.map(|_| Texture2D::create_empty_with_format::<F>(gl, width, height, tex_params))
|
||||
.map(|_| {
|
||||
Texture2D::create_empty_with_format::<F>(gl, width, height, tex_params)
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(Texture2DArray {
|
||||
textures: textures?,
|
||||
})
|
||||
Ok(Texture2DArray { textures: textures? })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -106,8 +106,9 @@ pub struct App {
|
||||
|
||||
ack_send: async_channel::Sender<ImageParams>,
|
||||
ack_recv: async_channel::Receiver<ImageParams>,
|
||||
|
||||
// callbacks
|
||||
//callback_position_changed: js_sys::Function,
|
||||
callback_position_changed: js_sys::Function,
|
||||
}
|
||||
|
||||
use cgmath::{Vector2, Vector3};
|
||||
@@ -130,7 +131,7 @@ impl App {
|
||||
mut shaders: ShaderManager,
|
||||
resources: Resources,
|
||||
// Callbacks
|
||||
//callback_position_changed: js_sys::Function,
|
||||
callback_position_changed: js_sys::Function,
|
||||
) -> Result<Self, JsValue> {
|
||||
let gl = gl.clone();
|
||||
//let exec = Rc::new(RefCell::new(TaskExecutor::new()));
|
||||
@@ -260,7 +261,8 @@ impl App {
|
||||
fits_recv,
|
||||
ack_send,
|
||||
ack_recv,
|
||||
//callback_position_changed,
|
||||
|
||||
callback_position_changed,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -538,12 +540,8 @@ impl App {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/*pub(crate) fn set_callback_position_changed(&mut self, callback: js_sys::Function) {
|
||||
pub(crate) fn set_callback_position_changed(&mut self, callback: js_sys::Function) {
|
||||
self.callback_position_changed = callback;
|
||||
}*/
|
||||
|
||||
pub(crate) fn is_inerting(&self) -> bool {
|
||||
return self.inertia.is_some();
|
||||
}
|
||||
|
||||
pub(crate) fn update(&mut self, _dt: DeltaTime) -> Result<bool, JsValue> {
|
||||
@@ -562,7 +560,7 @@ impl App {
|
||||
let cur_speed = inertia.get_cur_speed();
|
||||
|
||||
// Create the javascript object to pass to the callback
|
||||
/*let args: js_sys::Object = js_sys::Object::new();
|
||||
let args: js_sys::Object = js_sys::Object::new();
|
||||
let center = self.camera.get_center().lonlat();
|
||||
js_sys::Reflect::set(
|
||||
&args,
|
||||
@@ -580,7 +578,6 @@ impl App {
|
||||
// Position has changed, we call the callback
|
||||
self.callback_position_changed
|
||||
.call1(&JsValue::null(), &args)?;
|
||||
*/
|
||||
|
||||
if cur_speed < thresh_speed {
|
||||
self.inertia = None;
|
||||
@@ -609,11 +606,11 @@ impl App {
|
||||
for rsc in rscs_received {
|
||||
match rsc {
|
||||
Resource::Tile(tile) => {
|
||||
if !_has_camera_zoomed {
|
||||
if !has_camera_moved {
|
||||
if let Some(survey) =
|
||||
self.layers.get_mut_hips_from_cdid(&tile.get_hips_cdid())
|
||||
{
|
||||
let cfg = survey.get_config_mut();
|
||||
let cfg = survey.get_config();
|
||||
|
||||
if cfg.get_format() == tile.format {
|
||||
let delta_depth = cfg.delta_depth();
|
||||
@@ -652,63 +649,6 @@ impl App {
|
||||
} else {
|
||||
Some(image)
|
||||
};
|
||||
use al_core::image::ImageType;
|
||||
use fitsrs::fits::Fits;
|
||||
use std::{io::Cursor, rc::Rc};
|
||||
if let Some(image) = image.as_ref() {
|
||||
match &*image.lock().unwrap_abort() {
|
||||
Some(ImageType::FitsImage {
|
||||
raw_bytes: raw_bytes_buf,
|
||||
}) => {
|
||||
// check if the metadata has not been set
|
||||
if !cfg.fits_metadata {
|
||||
let num_bytes =
|
||||
raw_bytes_buf.length() as usize;
|
||||
let mut raw_bytes = vec![0; num_bytes];
|
||||
raw_bytes_buf.copy_to(&mut raw_bytes[..]);
|
||||
|
||||
let mut bytes_reader =
|
||||
Cursor::new(raw_bytes.as_slice());
|
||||
let Fits { hdu } =
|
||||
Fits::from_reader(&mut bytes_reader)
|
||||
.map_err(|_| {
|
||||
JsValue::from_str(
|
||||
"Parsing fits error",
|
||||
)
|
||||
})?;
|
||||
|
||||
let header = hdu.get_header();
|
||||
let bscale = if let Some(
|
||||
fitsrs::card::Value::Float(bscale),
|
||||
) = header.get(b"BSCALE ")
|
||||
{
|
||||
*bscale as f32
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
let bzero = if let Some(
|
||||
fitsrs::card::Value::Float(bzero),
|
||||
) = header.get(b"BZERO ")
|
||||
{
|
||||
*bzero as f32
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
let blank = if let Some(
|
||||
fitsrs::card::Value::Float(blank),
|
||||
) = header.get(b"BLANK ")
|
||||
{
|
||||
*blank as f32
|
||||
} else {
|
||||
std::f32::NAN
|
||||
};
|
||||
|
||||
cfg.set_fits_metadata(bscale, bzero, blank);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
survey.add_tile(&cell, image, time_req)?;
|
||||
self.request_redraw = true;
|
||||
@@ -881,10 +821,6 @@ impl App {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn draw_grid_labels(&mut self) -> Result<(), JsValue> {
|
||||
self.grid.draw_labels(&self.camera)
|
||||
}
|
||||
|
||||
pub(crate) fn draw(&mut self, force_render: bool) -> Result<(), JsValue> {
|
||||
/*let scene_redraw = self.rendering | force_render;
|
||||
let mut ui = self.ui.lock();
|
||||
@@ -1027,10 +963,10 @@ impl App {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn add_image_hips(&mut self, hips_cfg: HiPSCfg) -> Result<(), JsValue> {
|
||||
pub(crate) fn add_image_survey(&mut self, hips_cfg: HiPSCfg) -> Result<(), JsValue> {
|
||||
let hips =
|
||||
self.layers
|
||||
.add_image_hips(&self.gl, hips_cfg, &mut self.camera, &self.projection)?;
|
||||
.add_image_survey(&self.gl, hips_cfg, &mut self.camera, &self.projection)?;
|
||||
self.tile_fetcher
|
||||
.launch_starting_hips_requests(hips, &mut self.downloader);
|
||||
|
||||
@@ -1561,10 +1497,6 @@ impl App {
|
||||
self.request_redraw = true;
|
||||
}
|
||||
|
||||
pub(crate) fn set_inertia(&mut self, inertia: bool) {
|
||||
*self.disable_inertia.borrow_mut() = !inertia;
|
||||
}
|
||||
|
||||
/*pub(crate) fn project_line(&self, lon1: f64, lat1: f64, lon2: f64, lat2: f64) -> Vec<Vector2<f64>> {
|
||||
let v1: Vector3<f64> = LonLatT::new(ArcDeg(lon1).into(), ArcDeg(lat1).into()).vector();
|
||||
let v2: Vector3<f64> = LonLatT::new(ArcDeg(lon2).into(), ArcDeg(lat2).into()).vector();
|
||||
|
||||
@@ -11,7 +11,6 @@ use crate::healpix::cell::HEALPixCell;
|
||||
use crate::healpix::coverage::HEALPixCoverage;
|
||||
use crate::math::angle::ToAngle;
|
||||
use crate::math::{projection::coo_space::XYZWModel, projection::domain::sdf::ProjDef};
|
||||
use al_core::log::console_log;
|
||||
use al_core::{info, inforec, log};
|
||||
|
||||
use cgmath::{Matrix4, Vector2};
|
||||
@@ -381,8 +380,6 @@ impl CameraViewPort {
|
||||
}
|
||||
};
|
||||
|
||||
//console_log(&format!("clip factor {:?}", self.aperture));
|
||||
|
||||
// Project this vertex into the screen
|
||||
self.moved = true;
|
||||
self.zoomed = true;
|
||||
|
||||
@@ -2,7 +2,6 @@ pub mod label;
|
||||
pub mod meridian;
|
||||
pub mod parallel;
|
||||
|
||||
use crate::grid::parallel::Parallel;
|
||||
use crate::math::projection::coo_space::XYScreen;
|
||||
use crate::Abort;
|
||||
|
||||
@@ -32,9 +31,6 @@ pub struct ProjetedGrid {
|
||||
fmt: angle::SerializeFmt,
|
||||
|
||||
line_style: line::Style,
|
||||
|
||||
meridians: Vec<Meridian>,
|
||||
parallels: Vec<Parallel>,
|
||||
}
|
||||
|
||||
use crate::shader::ShaderManager;
|
||||
@@ -44,8 +40,6 @@ use crate::renderable::line::RasterizedLineRenderer;
|
||||
use crate::renderable::text::TextRenderManager;
|
||||
use web_sys::HtmlElement;
|
||||
|
||||
use self::meridian::Meridian;
|
||||
|
||||
impl ProjetedGrid {
|
||||
pub fn new(aladin_div: &HtmlElement) -> Result<ProjetedGrid, JsValue> {
|
||||
let text_renderer = TextRenderManager::new(aladin_div)?;
|
||||
@@ -62,8 +56,6 @@ impl ProjetedGrid {
|
||||
let line_style = line::Style::None;
|
||||
let fmt = angle::SerializeFmt::DMS;
|
||||
let thickness = 2.0;
|
||||
let meridians = Vec::new();
|
||||
let parallels = Vec::new();
|
||||
|
||||
let grid = ProjetedGrid {
|
||||
color,
|
||||
@@ -74,8 +66,6 @@ impl ProjetedGrid {
|
||||
thickness,
|
||||
|
||||
text_renderer,
|
||||
meridians,
|
||||
parallels,
|
||||
fmt,
|
||||
};
|
||||
// Initialize the vertices & labels
|
||||
@@ -136,9 +126,9 @@ impl ProjetedGrid {
|
||||
if let Some(enabled) = enabled {
|
||||
self.enabled = enabled;
|
||||
|
||||
/*if !self.enabled {
|
||||
if !self.enabled {
|
||||
self.text_renderer.clear_text_canvas();
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -157,7 +147,7 @@ impl ProjetedGrid {
|
||||
let step_line_px = max_dim_px * 0.2;
|
||||
|
||||
// update meridians
|
||||
self.meridians = {
|
||||
let meridians = {
|
||||
// Select the good step with a binary search
|
||||
let step_lon_precised =
|
||||
(bbox.get_lon_size() as f64) * step_line_px / (camera.get_width() as f64);
|
||||
@@ -183,7 +173,7 @@ impl ProjetedGrid {
|
||||
meridians
|
||||
};
|
||||
|
||||
self.parallels = {
|
||||
let parallels = {
|
||||
let step_lat_precised =
|
||||
(bbox.get_lat_size() as f64) * step_line_px / (camera.get_height() as f64);
|
||||
let step_lat = select_fixed_step(step_lat_precised);
|
||||
@@ -206,12 +196,11 @@ impl ProjetedGrid {
|
||||
};
|
||||
|
||||
// update the line buffers
|
||||
let paths = self
|
||||
.meridians
|
||||
let paths = meridians
|
||||
.iter()
|
||||
.map(|meridian| meridian.get_lines_vertices())
|
||||
.chain(
|
||||
self.parallels
|
||||
parallels
|
||||
.iter()
|
||||
.map(|parallel| parallel.get_lines_vertices()),
|
||||
)
|
||||
@@ -224,16 +213,12 @@ impl ProjetedGrid {
|
||||
let m = camera.get_screen_size().magnitude();
|
||||
rasterizer.add_stroke_paths(paths, self.thickness, &self.color, &self.line_style);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn draw_labels(&mut self, camera: &CameraViewPort) -> Result<(), JsValue> {
|
||||
if self.enabled && self.show_labels {
|
||||
let labels = self
|
||||
.meridians
|
||||
// update labels
|
||||
if self.show_labels {
|
||||
let labels = meridians
|
||||
.iter()
|
||||
.filter_map(|m| m.get_label())
|
||||
.chain(self.parallels.iter().filter_map(|p| p.get_label()));
|
||||
.chain(parallels.iter().filter_map(|p| p.get_label()));
|
||||
|
||||
let dpi = camera.get_dpi();
|
||||
self.text_renderer.begin();
|
||||
|
||||
@@ -73,7 +73,6 @@ extern "C" {
|
||||
#[macro_use]
|
||||
mod utils;
|
||||
|
||||
use al_core::log::console_log;
|
||||
use math::projection::*;
|
||||
use renderable::coverage::moc::MOC;
|
||||
//use votable::votable::VOTableWrapper;
|
||||
@@ -166,10 +165,13 @@ impl WebClient {
|
||||
let shaders = ShaderManager::new(&gl, shaders).unwrap_abort();
|
||||
|
||||
// Event listeners callbacks
|
||||
//let callback_position_changed = js_sys::Function::new_no_args("");
|
||||
let callback_position_changed = js_sys::Function::new_no_args("");
|
||||
let app = App::new(
|
||||
&gl, aladin_div, shaders, resources,
|
||||
//callback_position_changed,
|
||||
&gl,
|
||||
aladin_div,
|
||||
shaders,
|
||||
resources,
|
||||
callback_position_changed,
|
||||
)?;
|
||||
|
||||
let dt = DeltaTime::zero();
|
||||
@@ -179,14 +181,9 @@ impl WebClient {
|
||||
Ok(webclient)
|
||||
}
|
||||
|
||||
/*#[wasm_bindgen(js_name = setCallbackPositionChanged)]
|
||||
#[wasm_bindgen(js_name = setCallbackPositionChanged)]
|
||||
pub fn set_callback_position_changed(&mut self, callback: js_sys::Function) {
|
||||
self.app.set_callback_position_changed(callback);
|
||||
}*/
|
||||
|
||||
#[wasm_bindgen(js_name = isInerting)]
|
||||
pub fn is_inerting(&self) -> bool {
|
||||
return self.app.is_inerting();
|
||||
}
|
||||
|
||||
/// Update the view
|
||||
@@ -366,11 +363,11 @@ impl WebClient {
|
||||
/// * If the number of surveys is greater than 4. For the moment, due to the limitations
|
||||
/// of WebGL2 texture units on some architectures, the total number of surveys rendered is
|
||||
/// limited to 4.
|
||||
#[wasm_bindgen(js_name = addImageHiPS)]
|
||||
pub fn add_image_hips(&mut self, hips: JsValue) -> Result<(), JsValue> {
|
||||
#[wasm_bindgen(js_name = addImageSurvey)]
|
||||
pub fn add_image_survey(&mut self, hips: JsValue) -> Result<(), JsValue> {
|
||||
// Deserialize the survey objects that compose the survey
|
||||
let hips = serde_wasm_bindgen::from_value(hips)?;
|
||||
self.app.add_image_hips(hips)?;
|
||||
self.app.add_image_survey(hips)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -445,7 +442,7 @@ impl WebClient {
|
||||
/// * `green` - Green amount (between 0.0 and 1.0)
|
||||
/// * `blue` - Blue amount (between 0.0 and 1.0)
|
||||
/// * `alpha` - Alpha amount (between 0.0 and 1.0)
|
||||
#[wasm_bindgen(js_name = setGridOptions)]
|
||||
#[wasm_bindgen(js_name = setGridConfig)]
|
||||
pub fn set_grid_cfg(&mut self, cfg: JsValue) -> Result<(), JsValue> {
|
||||
let cfg = serde_wasm_bindgen::from_value(cfg)?;
|
||||
|
||||
@@ -487,13 +484,6 @@ impl WebClient {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = setInertia)]
|
||||
pub fn set_inertia(&mut self, inertia: bool) -> Result<(), JsValue> {
|
||||
self.app.set_inertia(inertia);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the absolute orientation of the view
|
||||
///
|
||||
/// # Arguments
|
||||
@@ -937,11 +927,6 @@ impl WebClient {
|
||||
self.app.is_rendering()
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = drawGridLabels)]
|
||||
pub fn draw_grid_labels(&mut self) -> Result<(), JsValue> {
|
||||
self.app.draw_grid_labels()
|
||||
}
|
||||
|
||||
#[wasm_bindgen(js_name = parseVOTable)]
|
||||
pub fn parse_votable(&mut self, s: &str) -> Result<JsValue, JsValue> {
|
||||
/*let votable: VOTableWrapper<votable::impls::mem::InMemTableDataRows> =
|
||||
|
||||
@@ -12,7 +12,6 @@ use al_core::image::Image;
|
||||
use al_core::log::console_log;
|
||||
use al_core::shader::Shader;
|
||||
use al_core::webgl_ctx::GlWrapper;
|
||||
use al_core::Texture2DArray;
|
||||
use al_core::VecData;
|
||||
use al_core::VertexArrayObject;
|
||||
use al_core::WebGlContext;
|
||||
@@ -44,7 +43,6 @@ use uv::{TileCorner, TileUVW};
|
||||
|
||||
use cgmath::{Matrix, Matrix4};
|
||||
use std::fmt::Debug;
|
||||
use std::rc::Rc;
|
||||
use wasm_bindgen::JsValue;
|
||||
use web_sys::WebGl2RenderingContext;
|
||||
|
||||
@@ -383,7 +381,11 @@ pub struct HiPS {
|
||||
}
|
||||
|
||||
impl HiPS {
|
||||
pub fn new(config: HiPSConfig, gl: &WebGlContext) -> Result<Self, JsValue> {
|
||||
pub fn new(
|
||||
config: HiPSConfig,
|
||||
gl: &WebGlContext,
|
||||
_camera: &CameraViewPort,
|
||||
) -> Result<Self, JsValue> {
|
||||
let mut vao = VertexArrayObject::new(gl);
|
||||
|
||||
// layout (location = 0) in vec2 lonlat;
|
||||
@@ -495,6 +497,8 @@ impl HiPS {
|
||||
.unbind();
|
||||
|
||||
let num_idx = 0;
|
||||
//let min_depth_tile = config.get_min_depth_tile();
|
||||
|
||||
let textures = ImageSurveyTextures::new(gl, config)?;
|
||||
|
||||
let gl = gl.clone();
|
||||
|
||||
@@ -10,7 +10,6 @@ pub mod utils;
|
||||
use crate::renderable::image::Image;
|
||||
|
||||
use al_core::image::format::ChannelType;
|
||||
use al_core::Texture2DArray;
|
||||
pub use hips::HiPS;
|
||||
|
||||
pub use catalog::Manager;
|
||||
@@ -21,7 +20,6 @@ use al_api::hips::ImageMetadata;
|
||||
use al_api::image::ImageParams;
|
||||
|
||||
use al_core::colormap::Colormaps;
|
||||
use al_core::image::format::NUM_CHANNELS;
|
||||
use al_core::shader::Shader;
|
||||
use al_core::SliceData;
|
||||
use al_core::VertexArrayObject;
|
||||
@@ -40,7 +38,6 @@ use hips::raytracing::RayTracer;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use wasm_bindgen::JsValue;
|
||||
use web_sys::WebGl2RenderingContext;
|
||||
|
||||
@@ -424,7 +421,7 @@ impl Layers {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_image_hips(
|
||||
pub fn add_image_survey(
|
||||
&mut self,
|
||||
gl: &WebGlContext,
|
||||
hips: HiPSCfg,
|
||||
@@ -484,7 +481,7 @@ impl Layers {
|
||||
}*/
|
||||
camera.register_view_frame(cfg.get_frame(), proj);
|
||||
|
||||
let hips = HiPS::new(cfg, gl)?;
|
||||
let hips = HiPS::new(cfg, gl, camera)?;
|
||||
// add the frame to the camera
|
||||
|
||||
self.surveys.insert(creator_did.clone(), hips);
|
||||
@@ -616,6 +613,16 @@ impl Layers {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/*pub fn is_ready(&self) -> bool {
|
||||
let ready = self
|
||||
.surveys
|
||||
.iter()
|
||||
.map(|(_, survey)| survey.is_ready())
|
||||
.fold(true, |acc, x| acc & x);
|
||||
|
||||
ready
|
||||
}*/
|
||||
|
||||
// Accessors
|
||||
// HiPSes getters
|
||||
pub fn get_hips_from_layer(&self, layer: &str) -> Option<&HiPS> {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use super::Renderer;
|
||||
use al_core::log::console_log;
|
||||
use web_sys::CanvasRenderingContext2d;
|
||||
|
||||
pub struct TextRenderManager {
|
||||
@@ -26,7 +25,7 @@ impl TextRenderManager {
|
||||
pub fn new(aladin_div: &HtmlElement) -> Result<Self, JsValue> {
|
||||
let canvas = aladin_div
|
||||
// Inside it, retrieve the canvas
|
||||
.get_elements_by_class_name("aladin-catalogCanvas")
|
||||
.get_elements_by_class_name("aladin-gridCanvas")
|
||||
.get_with_index(0)
|
||||
.unwrap_abort()
|
||||
.dyn_into::<web_sys::HtmlCanvasElement>()?;
|
||||
@@ -68,7 +67,6 @@ impl TextRenderManager {
|
||||
angle: A,
|
||||
) -> Result<(), JsValue> {
|
||||
self.ctx.save();
|
||||
|
||||
self.ctx
|
||||
.translate(screen_pos.x as f64, screen_pos.y as f64)?;
|
||||
|
||||
@@ -82,19 +80,37 @@ impl TextRenderManager {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Renderer for TextRenderManager {
|
||||
fn begin(&mut self) {
|
||||
//self.clear_text_canvas();
|
||||
// Clear the Aladin Lite 2d canvas
|
||||
// This canvas is where catalogs, grid labels, Hpx grid are drawn
|
||||
/*self.ctx.clear_rect(
|
||||
pub fn draw(
|
||||
&mut self,
|
||||
_camera: &CameraViewPort,
|
||||
_color: &ColorRGBA,
|
||||
_scale: f32,
|
||||
) -> Result<(), JsValue> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn clear_text_canvas(&mut self) {
|
||||
self.ctx.clear_rect(
|
||||
0_f64,
|
||||
0_f64,
|
||||
self.canvas.width() as f64,
|
||||
self.canvas.height() as f64,
|
||||
);*/
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl Renderer for TextRenderManager {
|
||||
fn begin(&mut self) {
|
||||
self.ctx = self
|
||||
.canvas
|
||||
.get_context("2d")
|
||||
.unwrap_abort()
|
||||
.unwrap_abort()
|
||||
.dyn_into::<web_sys::CanvasRenderingContext2d>()
|
||||
.unwrap_abort();
|
||||
|
||||
self.clear_text_canvas();
|
||||
|
||||
// reset the font and color
|
||||
self.ctx
|
||||
|
||||
@@ -138,7 +138,7 @@ pub struct ImageSurveyTextures {
|
||||
//pub cutoff_values_tile: Rc<RefCell<HashMap<HEALPixCell, (f32, f32)>>>,
|
||||
|
||||
// Array of 2D textures
|
||||
pub texture_2d_array: Texture2DArray,
|
||||
texture_2d_array: Texture2DArray,
|
||||
|
||||
// A boolean ensuring the root textures
|
||||
// have already been loaded
|
||||
@@ -168,7 +168,11 @@ fn create_texture_array<F: ImageFormat>(
|
||||
}
|
||||
|
||||
impl ImageSurveyTextures {
|
||||
pub fn new(gl: &WebGlContext, config: HiPSConfig) -> Result<ImageSurveyTextures, JsValue> {
|
||||
pub fn new(
|
||||
gl: &WebGlContext,
|
||||
config: HiPSConfig,
|
||||
//exec: Rc<RefCell<TaskExecutor>>,
|
||||
) -> Result<ImageSurveyTextures, JsValue> {
|
||||
let size = config.num_textures();
|
||||
// Ensures there is at least space for the 12
|
||||
// root textures
|
||||
@@ -208,6 +212,7 @@ impl ImageSurveyTextures {
|
||||
#[cfg(feature = "webgl2")]
|
||||
ChannelType::R64F => create_texture_array::<R64F>(gl, &config)?,
|
||||
};
|
||||
|
||||
// The root textures have not been loaded
|
||||
//let ready = false;
|
||||
//let num_root_textures_available = 0;
|
||||
@@ -379,20 +384,29 @@ impl ImageSurveyTextures {
|
||||
&mut self.base_textures[idx as usize]
|
||||
};*/
|
||||
|
||||
let missing = image.is_none();
|
||||
send_to_gpu(
|
||||
cell,
|
||||
texture,
|
||||
image,
|
||||
&self.texture_2d_array,
|
||||
&mut self.config,
|
||||
)?;
|
||||
|
||||
texture.append(
|
||||
cell, // The tile cell
|
||||
&self.config,
|
||||
missing,
|
||||
);
|
||||
if let Some(image) = image {
|
||||
send_to_gpu(cell, texture, image, &self.texture_2d_array, &self.config)?;
|
||||
// Once the texture has been received in the GPU
|
||||
texture.append(
|
||||
cell, // The tile cell
|
||||
&self.config,
|
||||
false,
|
||||
);
|
||||
} else {
|
||||
send_to_gpu(
|
||||
cell,
|
||||
texture,
|
||||
self.config.get_default_image(),
|
||||
&self.texture_2d_array,
|
||||
&self.config,
|
||||
)?;
|
||||
// Once the texture has been received in the GPU
|
||||
texture.append(
|
||||
cell, // The tile cell
|
||||
&self.config,
|
||||
true,
|
||||
);
|
||||
};
|
||||
|
||||
self.available_tiles_during_frame = true;
|
||||
//self.ready = true;
|
||||
@@ -624,9 +638,9 @@ impl ImageSurveyTextures {
|
||||
fn send_to_gpu<I: Image>(
|
||||
cell: &HEALPixCell,
|
||||
texture: &Texture,
|
||||
image: Option<I>,
|
||||
image: I,
|
||||
texture_array: &Texture2DArray,
|
||||
cfg: &mut HiPSConfig,
|
||||
cfg: &HiPSConfig,
|
||||
) -> Result<(), JsValue> {
|
||||
// Index of the texture in the total set of textures
|
||||
let texture_idx = texture.idx();
|
||||
@@ -658,12 +672,7 @@ fn send_to_gpu<I: Image>(
|
||||
idx_slice,
|
||||
);
|
||||
|
||||
if let Some(image) = image {
|
||||
image.tex_sub_image_3d(&texture_array, &offset)
|
||||
} else {
|
||||
cfg.get_default_image()
|
||||
.tex_sub_image_3d(&texture_array, &offset)
|
||||
}
|
||||
image.tex_sub_image_3d(&texture_array, &offset)
|
||||
}
|
||||
|
||||
impl SendUniforms for ImageSurveyTextures {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use al_api::hips::ImageExt;
|
||||
use al_core::log::console_log;
|
||||
use al_core::{image::format::ImageFormat, image::raw::ImageBuffer};
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -157,7 +156,6 @@ pub struct HiPSConfig {
|
||||
// TODO: store this values in the ImageSurvey
|
||||
// These are proper to the survey (FITS one) and not
|
||||
// to a specific survey color
|
||||
pub fits_metadata: bool,
|
||||
pub scale: f32,
|
||||
pub offset: f32,
|
||||
pub blank: f32,
|
||||
@@ -182,7 +180,7 @@ use wasm_bindgen::JsValue;
|
||||
|
||||
const NUM_TEXTURES_BY_SIDE_SLICE: i32 = 8;
|
||||
const NUM_TEXTURES_BY_SLICE: i32 = NUM_TEXTURES_BY_SIDE_SLICE * NUM_TEXTURES_BY_SIDE_SLICE;
|
||||
const NUM_SLICES: i32 = 1;
|
||||
const NUM_SLICES: i32 = 2;
|
||||
|
||||
impl HiPSConfig {
|
||||
/// Define a HiPS configuration
|
||||
@@ -330,7 +328,6 @@ impl HiPSConfig {
|
||||
|
||||
is_allsky,
|
||||
|
||||
fits_metadata: false,
|
||||
scale: 1.0,
|
||||
offset: 0.0,
|
||||
blank: -1.0, // by default, set it to -1
|
||||
@@ -452,7 +449,6 @@ impl HiPSConfig {
|
||||
self.scale = bscale;
|
||||
self.offset = bzero;
|
||||
self.blank = blank;
|
||||
self.fits_metadata = true;
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
position: relative;
|
||||
border: 0px solid #ddd;
|
||||
/* SVG inside divs add a 4px height: https://stackoverflow.com/questions/75751593/why-there-is-additional-4px-height-for-div-when-there-is-svg-inside-it */
|
||||
|
||||
/* disable x swipe on chrome, firefox */
|
||||
/* see. https://stackoverflow.com/questions/30636930/disable-web-page-navigation-on-swipeback-and-forward */
|
||||
overscroll-behavior-x: none;
|
||||
/* Hide the draggable boxes that goes out of the view */
|
||||
overflow: hidden;
|
||||
/* media query on the aladin lite container. not supported everywhere.
|
||||
There can be a more supported alternative here: https://caniuse.com/?search=grid-template-columns */
|
||||
/*container-type: inline-size;*/
|
||||
@@ -18,6 +17,12 @@
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.aladin-gridCanvas {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.aladin-catalogCanvas {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
@@ -49,6 +54,18 @@
|
||||
padding-top: 58.45%; /* aspect ratio of the background image */
|
||||
}
|
||||
|
||||
.aladin-col {
|
||||
float: left;
|
||||
width: 45.00%;
|
||||
margin-right: 5.0%;
|
||||
}
|
||||
/* Clear floats after the columns */
|
||||
.aladin-row:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.aladin-clipboard::before {
|
||||
content: ' 📋';
|
||||
cursor:pointer;
|
||||
@@ -96,30 +113,56 @@
|
||||
.aladin-measurement-div.aladin-dark-theme table thead {
|
||||
background-color: #000;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.aladin-measurement-div table td.aladin-href-td-container a:hover {
|
||||
overflow: visible;
|
||||
display: inline-block;
|
||||
animation: leftright 5s infinite normal linear;
|
||||
}
|
||||
|
||||
@keyframes leftright {
|
||||
0%,
|
||||
20% {
|
||||
transform: translateX(0%);
|
||||
left: 0%;
|
||||
}
|
||||
80%,
|
||||
100% {
|
||||
/* the max width is 150px and a padding of 0.8em is added for href link */
|
||||
transform: translateX(-100%);
|
||||
left: -100%;
|
||||
}
|
||||
}
|
||||
|
||||
.aladin-measurement-div table th {
|
||||
padding: 0.3em 0.5em;
|
||||
}
|
||||
|
||||
.aladin-measurement-div table tr td a {
|
||||
display: block;
|
||||
}
|
||||
.aladin-measurement-div table td.aladin-href-td-container {
|
||||
border: 1px solid #d2d2d2;
|
||||
|
||||
.aladin-measurement-div table tr td {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.aladin-measurement-div table tr td, .aladin-measurement-div table tr td a {
|
||||
max-width: 10rem;
|
||||
text-overflow: ellipsis;
|
||||
border-radius: 3px;
|
||||
|
||||
padding: 0.5em;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
/*max-width: 150px;
|
||||
text-overflow: ellipsis;*/
|
||||
}
|
||||
|
||||
word-wrap:break-word;
|
||||
.aladin-measurement-div table td.aladin-text-td-container {
|
||||
padding: 0.5em;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
|
||||
/*max-width: 150px;
|
||||
text-overflow: ellipsis;*/
|
||||
}
|
||||
|
||||
.aladin-measurement-div table td.aladin-href-td-container:hover {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.aladin-marker-measurement {
|
||||
@@ -135,6 +178,10 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.aladin-marker-measurement table tr td{
|
||||
word-wrap:break-word;
|
||||
}
|
||||
|
||||
.aladin-marker-measurement tr:nth-child(even) {
|
||||
background-color: #dddddd;
|
||||
}
|
||||
@@ -325,15 +372,16 @@ canvas {
|
||||
.aladin-btn.aladin-dark-theme.toggled {
|
||||
border-color: greenyellow;
|
||||
}
|
||||
.aladin-btn.aladin-dark-theme:hover, .aladin-input-select.aladin-dark-theme:hover {
|
||||
border-color: red;
|
||||
}
|
||||
|
||||
.aladin-btn.disabled {
|
||||
cursor: not-allowed;
|
||||
filter: brightness(70%);
|
||||
}
|
||||
|
||||
.aladin-btn:not(.disabled):hover {
|
||||
filter: brightness(105%);
|
||||
}
|
||||
|
||||
.aladin-btn.svg-icon {
|
||||
background-repeat: no-repeat;
|
||||
background-position:center center;
|
||||
@@ -344,13 +392,6 @@ canvas {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
}
|
||||
|
||||
.aladin-icon img {
|
||||
vertical-align:middle;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.aladin-icon.aladin-dark-theme {
|
||||
@@ -395,42 +436,43 @@ canvas {
|
||||
height: 1.7rem;
|
||||
}
|
||||
|
||||
.aladin-input-text.aladin-dark-theme:focus, .aladin-input-number.aladin-dark-theme:focus {
|
||||
border-color: dodgerblue;
|
||||
.aladin-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.aladin-input-text.aladin-dark-theme.search {
|
||||
width: 14rem;
|
||||
text-shadow: 0px 0px 2px #000;
|
||||
}
|
||||
|
||||
.aladin-input-text.search:focus, .aladin-input-text.search:hover {
|
||||
.aladin-input-text.aladin-dark-theme.search:focus, .aladin-input-text.aladin-dark-theme.search:hover {
|
||||
background-image: url(../../assets/icons/search-white.svg);
|
||||
background-size: 1.8rem;
|
||||
background-repeat: no-repeat;
|
||||
padding-left: 1.8rem;
|
||||
text-indent: 1.8rem;
|
||||
}
|
||||
|
||||
|
||||
.aladin-input-text.search {
|
||||
background-image:none;
|
||||
text-indent: 0rem;
|
||||
font-size: 1rem;
|
||||
line-height: 1.2rem;
|
||||
}
|
||||
|
||||
.aladin-input-text.search.aladin-not-valid {
|
||||
.aladin-input-text.search.aladin-unknownObject {
|
||||
-webkit-box-shadow:inset 0px 0px 0px 1px #f00;
|
||||
-moz-box-shadow:inset 0px 0px 0px 1px #f00;
|
||||
box-shadow:inset 0px 0px 0px 1px #f00;
|
||||
}
|
||||
|
||||
.aladin-input-text.aladin-dark-theme.aladin-not-valid {
|
||||
border: 1px solid red;
|
||||
}
|
||||
|
||||
.aladin-input-text.aladin-dark-theme.aladin-valid {
|
||||
border: 1px solid yellowgreen;
|
||||
}
|
||||
|
||||
.aladin-cancelBtn {
|
||||
background-color: #ca4242;
|
||||
border-color: #bd3935;
|
||||
@@ -466,12 +508,12 @@ canvas {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.aladin-vertical-list > *:first-child {
|
||||
.aladin-vertical-list > *:first-of-type {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.aladin-vertical-list > * {
|
||||
margin-top: 0.5rem;
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
|
||||
.aladin-horizontal-list {
|
||||
@@ -485,14 +527,11 @@ canvas {
|
||||
margin-right: 0.2rem;
|
||||
}
|
||||
|
||||
.aladin-horizontal-list > *:last-child {
|
||||
.aladin-horizontal-list > *::last-of-type {
|
||||
vertical-align: middle;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.aladin-form {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.aladin-form .aladin-form-input {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
@@ -500,17 +539,8 @@ canvas {
|
||||
margin-bottom: 0.4rem;
|
||||
}
|
||||
|
||||
.aladin-form .aladin-form-input:last-of-type {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.aladin-form .aladin-form-group {
|
||||
margin-bottom: 1rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.aladin-form .aladin-form-group:last-of-type {
|
||||
margin-bottom: 0rem;
|
||||
}
|
||||
|
||||
.aladin-form .aladin-form-input select {
|
||||
@@ -722,10 +752,6 @@ canvas {
|
||||
box-shadow:inset 1px 1px 0px 0px #fff;
|
||||
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
|
||||
margin: 0;
|
||||
box-sizing: content-box;
|
||||
line-height: normal;
|
||||
}
|
||||
|
||||
.aladin-context-menu .aladin-context-menu-item:first-of-type {
|
||||
@@ -812,7 +838,6 @@ canvas {
|
||||
border-radius: 5px;
|
||||
font-family: monospace;
|
||||
font-size: 1rem;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.aladin-input-text.aladin-dark-theme, .aladin-input-number.aladin-dark-theme {
|
||||
@@ -842,20 +867,18 @@ canvas {
|
||||
/* Remove focus outline */
|
||||
/* Remove IE arrow */
|
||||
}
|
||||
|
||||
.aladin-input-select option {
|
||||
color: inherit;
|
||||
background-color: #320a28;
|
||||
}
|
||||
|
||||
.aladin-input-select:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.aladin-input-select::-ms-expand {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
/* Frames */
|
||||
.aladin-input-color {
|
||||
appearance: none;
|
||||
@@ -885,17 +908,12 @@ canvas {
|
||||
/********** Range Input Styles **********/
|
||||
/*Range Reset*/
|
||||
.aladin-input-range {
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
width: 5em;
|
||||
height: 0.1rem;
|
||||
margin:0;
|
||||
margin: 1rem 0;
|
||||
}
|
||||
|
||||
|
||||
.aladin-input-range.aladin-reversed {
|
||||
direction: rtl;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
width: 5em;
|
||||
height: 1.5rem;
|
||||
}
|
||||
|
||||
/* Removes default focus */
|
||||
@@ -904,43 +922,47 @@ canvas {
|
||||
}
|
||||
|
||||
/***** Chrome, Safari, Opera and Edge Chromium styles *****/
|
||||
|
||||
.aladin-input-range::-webkit-slider-container {
|
||||
background: white;
|
||||
height: 0.1rem;
|
||||
min-height: 0.1rem;
|
||||
/* slider track */
|
||||
.aladin-input-range::-webkit-slider-runnable-track {
|
||||
background-color: #bababa;
|
||||
border-radius: 0.1rem;
|
||||
height: 0.1rem;
|
||||
}
|
||||
|
||||
/*
|
||||
.aladin-input-range-datalist {
|
||||
-webkit-appearance: none;
|
||||
/* slider thumb */
|
||||
.aladin-input-range::-webkit-slider-thumb {
|
||||
-webkit-appearance: none; /* Override default look */
|
||||
appearance: none;
|
||||
display: none;
|
||||
display: flex;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
padding:0;
|
||||
margin:0;
|
||||
height: 0.1rem;
|
||||
top: 0rem;
|
||||
pointer-events: none;
|
||||
}
|
||||
margin-top: -7px; /* Centers thumb on the track */
|
||||
|
||||
/*custom styles*/
|
||||
background-color: #bababa;
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
|
||||
|
||||
.aladin-input-range-datalist option {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
position: absolute;
|
||||
transform: translate(-50%, 0);
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
width: 0.1rem;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
/******** Firefox styles ********/
|
||||
/* slider track */
|
||||
.aladin-input-range::-moz-range-track {
|
||||
background-color: #bababa;
|
||||
border-radius: 0.1rem;
|
||||
height: 0.1rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background: #D3D3D3;
|
||||
}*/
|
||||
height: 0.1rem;
|
||||
}
|
||||
|
||||
/* slider thumb */
|
||||
.aladin-input-range::-moz-range-thumb {
|
||||
border: none; /*Removes extra border that FF applies*/
|
||||
border-radius: 0; /*Removes default border-radius that FF applies*/
|
||||
|
||||
/*custom styles*/
|
||||
background-color: #bababa;
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
.aladin-dark-theme {
|
||||
color: white;
|
||||
@@ -1096,29 +1118,6 @@ canvas {
|
||||
height: 1.7rem;
|
||||
}
|
||||
|
||||
/*
|
||||
.aladin-input-text.aladin-dark-theme.search.aladin-HiPS-search {
|
||||
width: 100%;
|
||||
|
||||
}*/
|
||||
|
||||
.aladin-stack-box {
|
||||
width: 17rem;
|
||||
}
|
||||
|
||||
.aladin-HiPS-filter-box {
|
||||
border: 1px solid white;
|
||||
}
|
||||
|
||||
.aladin-HiPS-browser-box .aladin-input-text {
|
||||
width: 300px;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.aladin-stack-box .aladin-input-select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.aladin-location {
|
||||
position: absolute;
|
||||
top: 0.2rem;
|
||||
@@ -1132,17 +1131,11 @@ canvas {
|
||||
height: 1.7rem;
|
||||
}
|
||||
|
||||
.aladin-location .aladin-input-text {
|
||||
width: 15rem;
|
||||
}
|
||||
|
||||
.aladin-fov {
|
||||
position: absolute;
|
||||
bottom: 0.2rem;
|
||||
left: 0.2rem;
|
||||
|
||||
background-color: red;
|
||||
|
||||
font-family: monospace;
|
||||
|
||||
font-size: 1rem;
|
||||
@@ -1150,16 +1143,6 @@ canvas {
|
||||
line-height: 1.7rem;
|
||||
}
|
||||
|
||||
.aladin-fov .aladin-zoom-in {
|
||||
margin-right: 0;
|
||||
border-right: none;
|
||||
border-radius: 5px 0px 0px 5px;
|
||||
}
|
||||
|
||||
.aladin-fov .aladin-zoom-out {
|
||||
border-radius: 0px 5px 5px 0px;
|
||||
}
|
||||
|
||||
.aladin-status-bar {
|
||||
border-radius: 3px;
|
||||
padding: 0.4rem;
|
||||
|
||||
207
src/js/A.js
207
src/js/A.js
@@ -33,7 +33,6 @@ import { Overlay } from "./Overlay.js";
|
||||
import { Circle } from "./Circle.js";
|
||||
import { Ellipse } from "./Ellipse.js";
|
||||
import { Polyline } from "./Polyline.js";
|
||||
import { Line } from "./Line.js";
|
||||
import { Catalog } from "./Catalog.js";
|
||||
import { ProgressiveCat } from "./ProgressiveCat.js";
|
||||
import { Source } from "./Source.js";
|
||||
@@ -106,13 +105,13 @@ A.aladin = function (divSelector, options) {
|
||||
* @function
|
||||
* @name A.imageHiPS
|
||||
* @memberof A
|
||||
* @param {string} id - Can be either an `url` that refers to a HiPS.
|
||||
* @param {string} id - Mandatory unique identifier for the survey.
|
||||
* @param {string} url - Can be an `url` that refers to a HiPS.
|
||||
* Or it can be a "CDS ID" pointing towards a HiPS. One can found the list of IDs {@link https://aladin.cds.unistra.fr/hips/list| here}.
|
||||
* @param {ImageHiPSOptions} [options] - Options describing the survey
|
||||
* @returns {ImageHiPS} - A HiPS image object
|
||||
* @param {ImageSurveyOptions} [options] - Options describing the survey
|
||||
* @returns {ImageSurvey} - A HiPS image object
|
||||
*/
|
||||
A.imageHiPS = function (id, options) {
|
||||
let url = id;
|
||||
A.imageHiPS = function (id, url, options) {
|
||||
return Aladin.createImageSurvey(
|
||||
id,
|
||||
options && options.name,
|
||||
@@ -131,7 +130,7 @@ A.imageHiPS = function (id, options) {
|
||||
* @memberof A
|
||||
* @param {string} url - Options describing the fits file. An url is mandatory
|
||||
* @param {ImageFITSOptions} [options] - Options describing the fits file. An url is mandatory
|
||||
* @returns {ImageFITS} - A HiPS image object
|
||||
* @returns {ImageSurvey} - A HiPS image object
|
||||
* @example
|
||||
* const sourceObj = A.source(180.0, 30.0, data, options);
|
||||
*/
|
||||
@@ -147,7 +146,7 @@ A.imageFITS = function (url, options) {
|
||||
* @memberof A
|
||||
* @param {number} ra - Right Ascension (RA) coordinate in degrees.
|
||||
* @param {number} dec - Declination (Dec) coordinate in degrees.
|
||||
* @param {Object} [data] - Additional data associated with the source.
|
||||
* @param {*} [data] - Additional data associated with the source.
|
||||
* @param {SourceOptions} [options] - Options for configuring the source object.
|
||||
* @returns {Source} A celestial source object.
|
||||
* @example
|
||||
@@ -166,7 +165,7 @@ A.source = function (ra, dec, data, options) {
|
||||
* @param {number} ra - Right Ascension (RA) coordinate in degrees.
|
||||
* @param {number} dec - Declination (Dec) coordinate in degrees.
|
||||
* @param {MarkerOptions} [options] - Options for configuring the marker.
|
||||
* @param {Object} [data] - Additional data associated with the marker.
|
||||
* @param {*} [data] - Additional data associated with the marker.
|
||||
* @returns {Source} A marker source object.
|
||||
* @example
|
||||
* const markerObj = A.marker(180.0, 30.0, data, options);
|
||||
@@ -184,11 +183,10 @@ A.marker = function (ra, dec, options, data) {
|
||||
* @memberof A
|
||||
* @name polygon
|
||||
*
|
||||
* @param {Array.<number[]>} radecArray - right-ascension/declination 2-tuple array describing the polyline's vertices in degrees
|
||||
* @param {ShapeOptions} options - Options for configuring the polygon
|
||||
* @param {Array} raDecArray - Array of celestial coordinates representing the vertices of the polygon.
|
||||
* Each element should be an object with properties `ra` (Right Ascension) in degrees and `dec` (Declination) in degrees.
|
||||
* @param {Object} options - Options for configuring the polygon.
|
||||
* @throws {string} Throws an error if the number of vertices is less than 3.
|
||||
*
|
||||
* @returns {Polyline}
|
||||
*/
|
||||
A.polygon = function (raDecArray, options) {
|
||||
const numVertices = raDecArray.length;
|
||||
@@ -213,16 +211,15 @@ A.polygon = function (raDecArray, options) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a polyline shape
|
||||
* Creates a polyline object using an array of celestial coordinates (RA, Dec).
|
||||
*
|
||||
* @function
|
||||
* @memberof A
|
||||
* @name polyline
|
||||
*
|
||||
* @param {Array.<number[]>} radecArray - right-ascension/declination 2-tuple array describing the polyline's vertices in degrees
|
||||
* @param {ShapeOptions} options - Options for configuring the polyline.
|
||||
*
|
||||
* @returns {Polyline}
|
||||
* @param {Array} raDecArray - Array of celestial coordinates representing the vertices of the polyline.
|
||||
* Each element should be an object with properties `ra` (Right Ascension) in degrees and `dec` (Declination) in degrees.
|
||||
* @param {Object} options - Options for configuring the polyline.
|
||||
*/
|
||||
A.polyline = function (raDecArray, options) {
|
||||
return new Polyline(raDecArray, options);
|
||||
@@ -230,7 +227,7 @@ A.polyline = function (raDecArray, options) {
|
||||
|
||||
|
||||
/**
|
||||
* Creates a circle shape
|
||||
* Creates a circle object
|
||||
*
|
||||
* @function
|
||||
* @memberof A
|
||||
@@ -240,15 +237,14 @@ A.polyline = function (raDecArray, options) {
|
||||
* @param {number} dec - Declination (Dec) coordinate of the center in degrees.
|
||||
* @param {number} radiusDeg - Radius in degrees.
|
||||
|
||||
* @param {ShapeOptions} options - Options for configuring the circle.
|
||||
* @returns {Circle}
|
||||
* @param {Object} options - Options for configuring the circle.
|
||||
*/
|
||||
A.circle = function (ra, dec, radiusDeg, options) {
|
||||
return new Circle([ra, dec], radiusDeg, options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates an ellipse shape
|
||||
* Creates a ellipse object
|
||||
*
|
||||
* @function
|
||||
* @memberof A
|
||||
@@ -260,55 +256,12 @@ A.circle = function (ra, dec, radiusDeg, options) {
|
||||
* @param {number} radiusDecDeg - the radius along the dec axis in degrees
|
||||
* @param {number} rotationDeg - the rotation angle in degrees
|
||||
|
||||
* @param {ShapeOptions} options - Options for configuring the ellipse.
|
||||
* @returns {Ellipse}
|
||||
* @param {Object} options - Options for configuring the ellipse.
|
||||
*/
|
||||
A.ellipse = function (ra, dec, radiusRaDeg, radiusDecDeg, rotationDeg, options) {
|
||||
return new Ellipse([ra, dec], radiusRaDeg, radiusDecDeg, rotationDeg, options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a line shape
|
||||
*
|
||||
* @function
|
||||
* @memberof A
|
||||
* @name line
|
||||
*
|
||||
* @param {number} ra1 - Right Ascension (RA) coordinate of the center in degrees.
|
||||
* @param {number} dec1 - Declination (Dec) coordinate of the center in degrees.
|
||||
* @param {number} ra2 - Right Ascension (RA) coordinate of the center in degrees.
|
||||
* @param {number} dec2 - Declination (Dec) coordinate of the center in degrees.
|
||||
* @param {CooFrame} [frame] - Frame in which the coordinates are given. If none, the frame used is icrs/j2000.
|
||||
* @param {ShapeOptions} options - Options for configuring the line.
|
||||
*
|
||||
* @returns {Line}
|
||||
*/
|
||||
A.line = function (ra1, dec1, ra2, dec2, frame, options) {
|
||||
return new Line(ra1, dec1, ra2, dec2, frame, options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a vector shape
|
||||
*
|
||||
* @function
|
||||
* @memberof A
|
||||
* @name vector
|
||||
*
|
||||
* @param {number} ra1 - Right Ascension (RA) coordinate of the center in degrees.
|
||||
* @param {number} dec1 - Declination (Dec) coordinate of the center in degrees.
|
||||
* @param {number} ra2 - Right Ascension (RA) coordinate of the center in degrees.
|
||||
* @param {number} dec2 - Declination (Dec) coordinate of the center in degrees.
|
||||
* @param {CooFrame} [frame] - Frame in which the coordinates are given. If none, the frame used is icrs/j2000.
|
||||
* @param {ShapeOptions} options - Options for configuring the vector.
|
||||
*
|
||||
* @returns {Line}
|
||||
*/
|
||||
A.vector = function (ra1, dec1, ra2, dec2, frame, options) {
|
||||
options['arrow'] = true;
|
||||
|
||||
return A.line(ra1, dec1, ra2, dec2, frame, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a graphic overlay on the Aladin Lite view.
|
||||
*
|
||||
@@ -364,68 +317,27 @@ A.coo = function (longitude, latitude, prec) {
|
||||
return new Coo(longitude, latitude, prec);
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse shapes from a STC-S string
|
||||
*
|
||||
* @function
|
||||
* @memberof A
|
||||
* @name footprintsFromSTCS
|
||||
*
|
||||
* @param {string} stcs - The STC-S string describing the shapes
|
||||
* @param {ShapeOptions} [options] - Options for the shape
|
||||
* @returns {Array.<Polyline|Circle>} Returns a list of shapes from the STC-S string
|
||||
*/
|
||||
// API
|
||||
A.footprint = function(shapes, source) {
|
||||
return new Footprint(shapes, source);
|
||||
};
|
||||
|
||||
// API
|
||||
A.footprintsFromSTCS = function (stcs, options) {
|
||||
var footprints = Overlay.parseSTCS(stcs, options);
|
||||
|
||||
return footprints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new MOC (Multi-Order-Coverage) from an url
|
||||
*
|
||||
* @function
|
||||
* @memberof A
|
||||
* @name MOCFromURL
|
||||
*
|
||||
* @param {string} url - The url to the MOC (e.g. stored as FITS file)
|
||||
* @param {MOCOptions} [options] - Display options for the MOC
|
||||
* @param {function} [successCallback] - Callback function when the MOC loads
|
||||
* @param {function} [errorCallback] - Callback function when the MOC fails loading
|
||||
* @returns {MOC} Returns a new MOC object
|
||||
*/
|
||||
A.MOCFromURL = function (url, options, successCallback, errorCallback) {
|
||||
// API
|
||||
A.MOCFromURL = function (url, options, successCallback) {
|
||||
var moc = new MOC(options);
|
||||
moc.parse(url, successCallback, errorCallback);
|
||||
moc.parse(url, successCallback);
|
||||
|
||||
return moc;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new MOC (Multi-Order-Coverage) from a JSON-like dictionary (javascript Object)
|
||||
*
|
||||
* @function
|
||||
* @memberof A
|
||||
* @name MOCFromJSON
|
||||
*
|
||||
* @param {Object} jsonMOC - The MOC stores as a JSON-like dictionary
|
||||
* @param {MOCOptions} [options] - Display options for the MOC
|
||||
* @param {function} [successCallback] - Callback function when the MOC loads
|
||||
* @param {function} [errorCallback] - Callback function when the MOC fails loading
|
||||
* @returns {MOC} Returns a new MOC object
|
||||
*
|
||||
* @example
|
||||
* var json = {"3":[517],
|
||||
* "4":[2065,2066,2067,2112,2344,2346,2432],
|
||||
* "5":[8221,8257,8258,8259,8293,8304,8305,8307,8308,8452,8456,9346,9352,9354,9736],
|
||||
* "6":[32861,32862,32863,32881,32882,32883,32892,32893,33025,33026,33027,33157,33168,33169,33171,
|
||||
* 33181,33224,33225,33227,33236,33240,33812,33816,33828,33832,37377,37378,37379,37382,37388,
|
||||
* 37390,37412,37414,37420,37422,37562,38928,38930,38936,38948,38952],
|
||||
* "7":[131423,131439,131443,131523,131556,131557,131580,131581,132099,132612,132613,132624,132625,132627,132637,
|
||||
* 132680,132681,132683,132709,132720,132721,132904,132905,132948,132952,132964,132968,133008,133009,133012,135252,135256,135268,135316,135320,135332,135336,148143,148152,148154,149507,149520
|
||||
* ,149522,149523,149652,149654,149660,149662,149684,149686,149692,149694,149695,150120,150122,150208,150210,150216,150218,150240,150242,150243,155748,155752,155796,155800,155812,155816]};
|
||||
* var moc = A.MOCFromJSON(json, {opacity: 0.25, color: 'magenta', lineWidth: 3});
|
||||
* aladin.addMOC(moc);
|
||||
*/
|
||||
// API
|
||||
A.MOCFromJSON = function (jsonMOC, options, successCallback, errorCallback) {
|
||||
var moc = new MOC(options);
|
||||
moc.parse(jsonMOC, successCallback, errorCallback);
|
||||
@@ -433,44 +345,14 @@ A.MOCFromJSON = function (jsonMOC, options, successCallback, errorCallback) {
|
||||
return moc;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new MOC (Multi-Order-Coverage) from an object describing a cone on the sky
|
||||
*
|
||||
* @function
|
||||
* @memberof A
|
||||
* @name MOCFromCone
|
||||
*
|
||||
* @param {Object} circle - A object describing a cone in the sky
|
||||
* @param {number} circle.ra - Right-ascension of the circle's center (in deg)
|
||||
* @param {number} circle.dec - Declination of the circle's center (in deg)
|
||||
* @param {number} circle.radius - Radius of the circle (in deg)
|
||||
* @param {MOCOptions} [options] - Display options for the MOC
|
||||
* @param {function} [successCallback] - Callback function when the MOC loads
|
||||
* @param {function} [errorCallback] - Callback function when the MOC fails loading
|
||||
* @returns {MOC} Returns a new MOC object
|
||||
*/
|
||||
A.MOCFromCone = function (circle, options, successCallback, errorCallback) {
|
||||
// API
|
||||
A.MOCFromCircle = function (circle, options, successCallback, errorCallback) {
|
||||
var moc = new MOC(options);
|
||||
moc.parse(circle, successCallback, errorCallback);
|
||||
|
||||
return moc;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new MOC (Multi-Order-Coverage) from an object describing a polygon on the sky
|
||||
*
|
||||
* @function
|
||||
* @memberof A
|
||||
* @name MOCFromPolygon
|
||||
*
|
||||
* @param {Object} polygon - A object describing a polygon in the sky
|
||||
* @param {number[]} polygon.ra - Right-ascensions of the polygon's vertices (in deg)
|
||||
* @param {number[]} polygon.dec - Declination of the polygon's vertices (in deg)
|
||||
* @param {MOCOptions} [options] - Display options for the MOC
|
||||
* @param {function} [successCallback] - Callback function when the MOC loads
|
||||
* @param {function} [errorCallback] - Callback function when the MOC fails loading
|
||||
* @returns {MOC} Returns a new MOC object
|
||||
*/
|
||||
A.MOCFromPolygon= function (polygon, options, successCallback, errorCallback) {
|
||||
var moc = new MOC(options);
|
||||
moc.parse(polygon, successCallback, errorCallback);
|
||||
@@ -553,24 +435,18 @@ A.catalogFromURL = function (url, options, successCallback, errorCallback, usePr
|
||||
options.url = url;
|
||||
var catalog = A.catalog(options);
|
||||
const processVOTable = function (table) {
|
||||
let {sources, fields} = table;
|
||||
let {sources, footprints, fields, type} = table;
|
||||
catalog.setFields(fields);
|
||||
catalog.addSources(sources);
|
||||
|
||||
if ('s_region' in fields && typeof catalog.shape !== 'function') {
|
||||
// set the shape
|
||||
catalog.setShape((s) => {
|
||||
if (!s.data.s_region)
|
||||
return;
|
||||
|
||||
const shapes = A.footprintsFromSTCS(s.data.s_region, options)
|
||||
let fp = new Footprint(shapes, s);
|
||||
fp.setColor(catalog.color);
|
||||
|
||||
return fp;
|
||||
})
|
||||
if (catalog.type === 'ObsCore') {
|
||||
// The fields corresponds to obscore ones
|
||||
// Set the name of the catalog to be ObsCore:<catalog name>
|
||||
catalog.name = "ObsCore:" + url;
|
||||
}
|
||||
|
||||
catalog.addFootprints(footprints)
|
||||
catalog.addSources(sources);
|
||||
|
||||
if (successCallback) {
|
||||
successCallback(catalog);
|
||||
}
|
||||
@@ -682,7 +558,7 @@ A.catalogFromSimbad = function (target, radius, options, successCallback, errorC
|
||||
}).then((coo) => {
|
||||
const url = URLBuilder.buildSimbadCSURL(coo.lon, coo.lat, radius, options)
|
||||
const processVOTable = function (table) {
|
||||
let {sources, fields} = table;
|
||||
let {sources, footprints, fields, type} = table;
|
||||
cat.setFields(fields);
|
||||
|
||||
if (cat.type === 'ObsCore') {
|
||||
@@ -691,6 +567,7 @@ A.catalogFromSimbad = function (target, radius, options, successCallback, errorC
|
||||
cat.name = "ObsCore:" + url;
|
||||
}
|
||||
|
||||
cat.addFootprints(footprints)
|
||||
cat.addSources(sources);
|
||||
|
||||
if (successCallback) {
|
||||
|
||||
1613
src/js/Aladin.js
1613
src/js/Aladin.js
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -29,27 +29,13 @@
|
||||
*****************************************************************************/
|
||||
|
||||
import { Utils } from "./Utils";
|
||||
import { AladinUtils } from "./AladinUtils.js";
|
||||
import { Overlay } from "./Overlay.js";
|
||||
|
||||
/**
|
||||
* Represents an circle shape
|
||||
*
|
||||
* @namespace
|
||||
* @typedef {Object} Circle
|
||||
*/
|
||||
// TODO : Circle and Footprint should inherit from the same root object
|
||||
export let Circle = (function() {
|
||||
/**
|
||||
* Constructor function for creating a new circle.
|
||||
*
|
||||
* @constructor
|
||||
* @memberof Circle
|
||||
* @param {number[]} center - right-ascension/declination 2-tuple of the circle's center in degrees
|
||||
* @param {number} radius - radius in degrees
|
||||
* @param {ShapeOptions} options - Configuration options for the circle
|
||||
*
|
||||
* @returns {Circle} - The circle shape object
|
||||
*/
|
||||
let Circle = function(center, radius, options) {
|
||||
// constructor
|
||||
let Circle = function(centerRaDec, radiusDegrees, options) {
|
||||
options = options || {};
|
||||
|
||||
this.color = options['color'] || undefined;
|
||||
@@ -61,8 +47,8 @@ export let Circle = (function() {
|
||||
// TODO : all graphic overlays should have an id
|
||||
this.id = 'circle-' + Utils.uuidv4();
|
||||
|
||||
this.setCenter(center);
|
||||
this.setRadius(radius);
|
||||
this.setCenter(centerRaDec);
|
||||
this.setRadius(radiusDegrees);
|
||||
this.overlay = null;
|
||||
|
||||
this.isShowing = true;
|
||||
@@ -71,7 +57,7 @@ export let Circle = (function() {
|
||||
};
|
||||
|
||||
Circle.prototype.setColor = function(color) {
|
||||
if (!color || this.color == color) {
|
||||
if (this.color == color) {
|
||||
return;
|
||||
}
|
||||
this.color = color;
|
||||
@@ -81,7 +67,7 @@ export let Circle = (function() {
|
||||
};
|
||||
|
||||
Circle.prototype.setSelectionColor = function(color) {
|
||||
if (!color || this.selectionColor == color) {
|
||||
if (this.selectionColor == color) {
|
||||
return;
|
||||
}
|
||||
this.selectionColor = color;
|
||||
@@ -91,7 +77,7 @@ export let Circle = (function() {
|
||||
};
|
||||
|
||||
Circle.prototype.setHoverColor = function(color) {
|
||||
if (!color || this.hoverColor == color) {
|
||||
if (this.hoverColor == color) {
|
||||
return;
|
||||
}
|
||||
this.hoverColor = color;
|
||||
@@ -199,15 +185,15 @@ export let Circle = (function() {
|
||||
// TODO
|
||||
Circle.prototype.draw = function(ctx, view, noStroke) {
|
||||
if (! this.isShowing) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
noStroke = noStroke===true || false;
|
||||
|
||||
var centerXyview = view.aladin.world2pix(this.centerRaDec[0], this.centerRaDec[1]);
|
||||
var centerXyview = AladinUtils.radecToViewXy(this.centerRaDec[0], this.centerRaDec[1], view.aladin);
|
||||
if (!centerXyview) {
|
||||
// the center goes out of the projection
|
||||
// we do not draw it
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
this.center = {
|
||||
x: centerXyview[0],
|
||||
@@ -217,27 +203,42 @@ export let Circle = (function() {
|
||||
let hidden = true;
|
||||
|
||||
var ra, dec, vertOnCircle, dx, dy;
|
||||
this.radius = Number.NEGATIVE_INFINITY;
|
||||
|
||||
// Project 4 points lying on the circle and take the minimal dist with the center as radius
|
||||
[[-1, 0], [1, 0], [0, -1], [0, 1]].forEach(([cardDirRa, cardDirDec]) => {
|
||||
ra = this.centerRaDec[0] + cardDirRa * this.radiusDegrees;
|
||||
dec = this.centerRaDec[1] + cardDirDec * this.radiusDegrees;
|
||||
//if (this.radiusDegrees > 30) {
|
||||
this.radius = Number.NEGATIVE_INFINITY;
|
||||
|
||||
// Project 4 points lying on the circle and take the minimal dist with the center as radius
|
||||
[[-1, 0], [1, 0], [0, -1], [0, 1]].forEach(([cardDirRa, cardDirDec]) => {
|
||||
ra = this.centerRaDec[0] + cardDirRa * this.radiusDegrees;
|
||||
dec = this.centerRaDec[1] + cardDirDec * this.radiusDegrees;
|
||||
|
||||
vertOnCircle = view.aladin.world2pix(ra, dec);
|
||||
vertOnCircle = AladinUtils.radecToViewXy(ra, dec, view.aladin);
|
||||
|
||||
if (vertOnCircle) {
|
||||
dx = vertOnCircle[0] - this.center.x;
|
||||
dy = vertOnCircle[1] - this.center.y;
|
||||
|
||||
this.radius = Math.max(Math.sqrt(dx*dx + dy*dy), this.radius);
|
||||
|
||||
hidden = false;
|
||||
}
|
||||
});
|
||||
/*} else {
|
||||
ra = this.centerRaDec[0] + this.radiusDegrees;
|
||||
dec = this.centerRaDec[1];
|
||||
|
||||
vertOnCircle = AladinUtils.radecToViewXy(ra, dec, view);
|
||||
|
||||
if (vertOnCircle) {
|
||||
dx = vertOnCircle[0] - this.center.x;
|
||||
dy = vertOnCircle[1] - this.center.y;
|
||||
|
||||
this.radius = Math.max(Math.sqrt(dx*dx + dy*dy), this.radius);
|
||||
|
||||
this.radius = Math.sqrt(dx*dx + dy*dy);
|
||||
hidden = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}*/
|
||||
|
||||
if (hidden) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
// Then we can draw
|
||||
|
||||
@@ -271,8 +272,6 @@ export let Circle = (function() {
|
||||
}
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
Circle.prototype.isInStroke = function(ctx, view, x, y) {
|
||||
|
||||
@@ -49,12 +49,7 @@ export let DefaultActionsForContextMenu = (function () {
|
||||
{
|
||||
label: "Copy position", action(o) {
|
||||
let msg;
|
||||
let text = o.target.innerText;
|
||||
if (!text) {
|
||||
return false;
|
||||
}
|
||||
|
||||
navigator.clipboard.writeText(text)
|
||||
navigator.clipboard.writeText(o.target.innerText)
|
||||
.then(() => {
|
||||
msg = 'successful'
|
||||
if (aladinInstance.statusBar) {
|
||||
|
||||
@@ -1,252 +0,0 @@
|
||||
// Copyright 2013 - UDS/CNRS
|
||||
// The Aladin Lite program is distributed under the terms
|
||||
// of the GNU General Public License version 3.
|
||||
//
|
||||
// This file is part of Aladin Lite.
|
||||
//
|
||||
// Aladin Lite is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 3 of the License.
|
||||
//
|
||||
// Aladin Lite is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// The GNU General Public License is available in COPYING file
|
||||
// along with Aladin Lite.
|
||||
//
|
||||
|
||||
/******************************************************************************
|
||||
* Aladin Lite project
|
||||
*
|
||||
* File ImageHiPS
|
||||
*
|
||||
* Authors: Thomas Boch & Matthieu Baumann [CDS]
|
||||
*
|
||||
*****************************************************************************/
|
||||
import { ALEvent } from "./events/ALEvent.js";
|
||||
|
||||
export let HiPSCache = (function () {
|
||||
function HiPSCache() {}
|
||||
|
||||
HiPSCache.append = function (key, image) {
|
||||
HiPSCache.cache[key] = image;
|
||||
|
||||
ALEvent.HIPS_CACHE_UPDATED.dispatchedTo(document.body);
|
||||
};
|
||||
|
||||
HiPSCache.delete = function (key) {
|
||||
delete HiPSCache.cache[key];
|
||||
|
||||
ALEvent.HIPS_CACHE_UPDATED.dispatchedTo(document.body);
|
||||
};
|
||||
|
||||
HiPSCache.get = function (key) {
|
||||
return HiPSCache.cache[key];
|
||||
};
|
||||
|
||||
// A cache storing directly surveys important information to not query for the properties each time
|
||||
HiPSCache.cache = {};
|
||||
|
||||
HiPSCache.DEFAULT_HIPS_LIST = [
|
||||
{
|
||||
creatorDid: "ivo://CDS/P/DSS2/color",
|
||||
name: "DSS colored",
|
||||
id: "CDS/P/DSS2/color",
|
||||
maxOrder: 9,
|
||||
tileSize: 512,
|
||||
imgFormat: "jpeg",
|
||||
cooFrame: "ICRS",
|
||||
},
|
||||
{
|
||||
creatorDid: "ivo://CDS/P/2MASS/color",
|
||||
name: "2MASS colored",
|
||||
id: "CDS/P/2MASS/color",
|
||||
maxOrder: 9,
|
||||
tileSize: 512,
|
||||
imgFormat: "jpeg",
|
||||
cooFrame: "ICRS",
|
||||
},
|
||||
{
|
||||
creatorDid: "ivo://CDS/P/DSS2/red",
|
||||
name: "DSS2 Red (F+R)",
|
||||
id: "CDS/P/DSS2/red",
|
||||
maxOrder: 9,
|
||||
tileSize: 512,
|
||||
imgFormat: "fits",
|
||||
cooFrame: "ICRS",
|
||||
numBitsPerPixel: 16,
|
||||
// options
|
||||
minCut: 1000.0,
|
||||
maxCut: 10000.0,
|
||||
colormap: "magma",
|
||||
stretch: "Linear",
|
||||
imgFormat: "fits",
|
||||
},
|
||||
{
|
||||
creatorDid: "ivo://CDS/P/DM/I/350/gaiaedr3",
|
||||
name: "Density map for Gaia EDR3 (I/350/gaiaedr3)",
|
||||
id: "CDS/P/DM/I/350/gaiaedr3",
|
||||
maxOrder: 7,
|
||||
tileSize: 512,
|
||||
numBitsPerPixel: -32,
|
||||
cooFrame: "ICRS",
|
||||
minCut: 0,
|
||||
maxCut: 12000,
|
||||
stretch: "asinh",
|
||||
colormap: "rdylbu",
|
||||
imgFormat: "fits",
|
||||
},
|
||||
{
|
||||
creatorDid: "ivo://CDS/P/PanSTARRS/DR1/g",
|
||||
name: "PanSTARRS DR1 g",
|
||||
id: "CDS/P/PanSTARRS/DR1/g",
|
||||
maxOrder: 11,
|
||||
tileSize: 512,
|
||||
imgFormat: "fits",
|
||||
cooFrame: "ICRS",
|
||||
numBitsPerPixel: -32,
|
||||
// options
|
||||
minCut: -34,
|
||||
maxCut: 7000,
|
||||
stretch: "asinh",
|
||||
colormap: "redtemperature",
|
||||
},
|
||||
{
|
||||
creatorDid: "ivo://CDS/P/PanSTARRS/DR1/color-z-zg-g",
|
||||
name: "PanSTARRS DR1 color",
|
||||
id: "CDS/P/PanSTARRS/DR1/color-z-zg-g",
|
||||
maxOrder: 11,
|
||||
tileSize: 512,
|
||||
imgFormat: "jpeg",
|
||||
cooFrame: "ICRS",
|
||||
},
|
||||
{
|
||||
creatorDid: "ivo://CDS/P/DECaPS/DR2/color",
|
||||
name: "DECaPS DR2 color",
|
||||
id: "CDS/P/DECaPS/DR2/color",
|
||||
maxOrder: 11,
|
||||
cooFrame: "equatorial",
|
||||
tileSize: 512,
|
||||
imgFormat: "png",
|
||||
},
|
||||
{
|
||||
creatorDid: "ivo://CDS/P/Fermi/color",
|
||||
name: "Fermi color",
|
||||
id: "CDS/P/Fermi/color",
|
||||
maxOrder: 3,
|
||||
imgFormat: "jpeg",
|
||||
tileSize: 512,
|
||||
cooFrame: "equatorial",
|
||||
},
|
||||
{
|
||||
creatorDid: "ivo://CDS/P/GALEXGR6_7/NUV",
|
||||
id: "P/GALEXGR6_7/NUV",
|
||||
name: "GALEXGR6_7 NUV",
|
||||
maxOrder: 8,
|
||||
imgFormat: "png",
|
||||
tileSize: 512,
|
||||
cooFrame: "equatorial",
|
||||
},
|
||||
{
|
||||
creatorDid: "ivo://CDS/P/IRIS/color",
|
||||
id: "CDS/P/IRIS/color",
|
||||
name: "IRIS colored",
|
||||
maxOrder: 3,
|
||||
tileSize: 256,
|
||||
imgFormat: "jpeg",
|
||||
cooFrame: "galactic",
|
||||
},
|
||||
{
|
||||
creatorDid: "ivo://CDS/P/Mellinger/color",
|
||||
id: "CDS/P/Mellinger/color",
|
||||
name: "Mellinger colored",
|
||||
maxOrder: 4,
|
||||
tileSize: 512,
|
||||
imgFormat: "jpeg",
|
||||
cooFrame: "galactic",
|
||||
},
|
||||
{
|
||||
creatorDid: "ivo://CDS/P/SDSS9/color",
|
||||
id: "CDS/P/SDSS9/color",
|
||||
name: "SDSS9 colored",
|
||||
maxOrder: 10,
|
||||
tileSize: 512,
|
||||
imgFormat: "jpeg",
|
||||
cooFrame: "equatorial",
|
||||
},
|
||||
{
|
||||
creatorDid: "ivo://CDS/P/SPITZER/color",
|
||||
id: "CDS/P/SPITZER/color",
|
||||
name: "IRAC color I1,I2,I4 - (GLIMPSE, SAGE, SAGE-SMC, SINGS)",
|
||||
maxOrder: 9,
|
||||
tileSize: 512,
|
||||
imgFormat: "jpeg",
|
||||
cooFrame: "galactic",
|
||||
},
|
||||
{
|
||||
creatorDid: "ivo://CDS/P/allWISE/color",
|
||||
id: "CDS/P/allWISE/color",
|
||||
name: "AllWISE color",
|
||||
maxOrder: 8,
|
||||
tileSize: 512,
|
||||
imgFormat: "jpeg",
|
||||
cooFrame: "equatorial",
|
||||
},
|
||||
{
|
||||
creatorDid: "ivo://CDS/P/SDSS9/g",
|
||||
id: "CDS/P/SDSS9/g",
|
||||
name: "SDSS9 band-g",
|
||||
maxOrder: 10,
|
||||
tileSize: 512,
|
||||
numBitsPerPixel: 16,
|
||||
imgFormat: "fits",
|
||||
cooFrame: "equatorial",
|
||||
minCut: 0,
|
||||
maxCut: 1.8,
|
||||
stretch: "linear",
|
||||
colormap: "redtemperature",
|
||||
},
|
||||
{
|
||||
id: "CDS/P/Finkbeiner",
|
||||
name: "Halpha",
|
||||
maxOrder: 3,
|
||||
minCut: -10,
|
||||
maxCut: 800,
|
||||
colormap: "rdbu",
|
||||
imgFormat: "fits",
|
||||
},
|
||||
{
|
||||
id: "CDS/P/VTSS/Ha",
|
||||
name: "VTSS-Ha",
|
||||
maxOrder: 3,
|
||||
minCut: -10.0,
|
||||
maxCut: 100.0,
|
||||
colormap: "grayscale",
|
||||
imgFormat: "fits",
|
||||
},
|
||||
{
|
||||
id: "xcatdb/P/XMM/PN/color",
|
||||
name: "XMM PN colored",
|
||||
maxOrder: 7,
|
||||
},
|
||||
{
|
||||
id: "CDS/P/allWISE/color",
|
||||
name: "AllWISE color",
|
||||
maxOrder: 8,
|
||||
},
|
||||
/*{
|
||||
id: "CDS/P/GLIMPSE360",
|
||||
name: "GLIMPSE360",
|
||||
// This domain is not giving the CORS headers
|
||||
// We need to query by with a proxy equipped with CORS header.
|
||||
//url: "https://alasky.cds.unistra.fr/cgi/JSONProxy?url=https://www.spitzer.caltech.edu/glimpse360/aladin/data",
|
||||
maxOrder: 9,
|
||||
imgFormat: "jpeg",
|
||||
minOrder: 3,
|
||||
}*/
|
||||
];
|
||||
|
||||
return HiPSCache;
|
||||
})();
|
||||
130
src/js/DiscoveryTree.js
Normal file
130
src/js/DiscoveryTree.js
Normal file
@@ -0,0 +1,130 @@
|
||||
import A from "./A.js";
|
||||
|
||||
export let DiscoveryTree = (function () {
|
||||
// Constructor
|
||||
var DiscoveryTree = function (aladin) {
|
||||
// activate Vue on the <div> that contains the component
|
||||
new Vue({
|
||||
el: '#ui',
|
||||
methods: {
|
||||
// Define the methods for the discovery-tree component
|
||||
// to interact with the aladin viewer
|
||||
getFovCorners() {
|
||||
return aladin.getFovCorners();
|
||||
},
|
||||
getCenter() {
|
||||
return aladin.getRaDec();
|
||||
},
|
||||
// Called when the user add a image survey
|
||||
addImage(metadata) {
|
||||
const order = (+metadata.hips_order);
|
||||
const hipsTileFormat = metadata.hips_tile_format.split(' ');
|
||||
|
||||
let tileFormat;
|
||||
let color;
|
||||
if (hipsTileFormat.indexOf('fits') >= 0) {
|
||||
tileFormat = {
|
||||
FITSImage: {
|
||||
bitpix: parseInt(metadata.hips_pixel_bitpix)
|
||||
}
|
||||
};
|
||||
color = {
|
||||
Grayscale2Color: {
|
||||
color: [1.0, 1.0, 1.0],
|
||||
k: 1.0,
|
||||
transfer: "asinh"
|
||||
}
|
||||
};
|
||||
} else {
|
||||
color = "Color";
|
||||
|
||||
if (hipsTileFormat.indexOf('png') >= 0) {
|
||||
tileFormat = {
|
||||
Image: {
|
||||
format: "png"
|
||||
}
|
||||
};
|
||||
} else {
|
||||
tileFormat = {
|
||||
Image: {
|
||||
format: "jpeg"
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let cuts = [undefined, undefined];
|
||||
if (metadata.hips_pixel_cut) {
|
||||
cuts = metadata.hips_pixel_cut.split(" ");
|
||||
}
|
||||
let tileSize = 512;
|
||||
// Verify the validity of the tile width
|
||||
if (metadata.hips_tile_width) {
|
||||
let hipsTileWidth = parseInt(metadata.hips_tile_width);
|
||||
let isPowerOfTwo = hipsTileWidth && !(hipsTileWidth & (hipsTileWidth - 1));
|
||||
|
||||
if (isPowerOfTwo === true) {
|
||||
tileSize = hipsTileWidth;
|
||||
}
|
||||
}
|
||||
let url = metadata.hips_service_url;
|
||||
if (url.startsWith('http://alasky')) {
|
||||
// From alasky one can directly use the https access
|
||||
url = url.replace('http', 'https');
|
||||
} else {
|
||||
// Pass by a proxy for extern http urls
|
||||
url = 'https://alasky.u-strasbg.fr/cgi/JSONProxy?url=' + url;
|
||||
}
|
||||
let survey = {
|
||||
properties: {
|
||||
url: url,
|
||||
maxOrder: parseInt(metadata.hips_order),
|
||||
frame: {
|
||||
label: "J2000",
|
||||
system: "J2000"
|
||||
},
|
||||
tileSize: tileSize,
|
||||
format: tileFormat,
|
||||
minCutout: parseFloat(cuts[0]),
|
||||
maxCutout: parseFloat(cuts[1]),
|
||||
},
|
||||
color: color
|
||||
};
|
||||
|
||||
aladin.setImageSurveysLayer([survey], "base");
|
||||
},
|
||||
// Called when the user add a catalog survey
|
||||
addCatalog(metadata, center, radius) {
|
||||
if (metadata.hips_service_url) {
|
||||
const hips = A.catalogHiPS(metadata.hips_service_url, {
|
||||
onClick: 'showTable',
|
||||
name: metadata.ID,
|
||||
});
|
||||
aladin.addCatalog(hips);
|
||||
} else {
|
||||
console.log(metadata.obs_id, "center, ", center, " radius, ", radius)
|
||||
const catalog = A.catalogFromVizieR(
|
||||
metadata.obs_id,
|
||||
{
|
||||
ra: center[0],
|
||||
dec: center[1]
|
||||
},
|
||||
radius, {
|
||||
onClick: 'showTable',
|
||||
limit: 1000,
|
||||
}
|
||||
);
|
||||
aladin.addCatalog(catalog);
|
||||
}
|
||||
},
|
||||
// Called when the user add a HEALPix coverage
|
||||
addCoverage(metadata) {
|
||||
const moc = A.MOCFromURL(metadata.moc_access_url);
|
||||
aladin.addMOC(moc);
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return DiscoveryTree;
|
||||
})();
|
||||
@@ -29,44 +29,13 @@
|
||||
*****************************************************************************/
|
||||
|
||||
import { Utils } from "./Utils";
|
||||
import { AladinUtils } from "./AladinUtils.js";
|
||||
import { Overlay } from "./Overlay.js";
|
||||
import { requestAnimFrame } from "./libs/RequestAnimationFrame";
|
||||
|
||||
/**
|
||||
* @typedef {Object} ShapeOptions
|
||||
* @description Options for describing a shape
|
||||
*
|
||||
* @property {Object} options - Configuration options for the shape.
|
||||
* @property {string} [options.color] - The color of the shape
|
||||
* @property {string} [options.fill=false] - Fill the shape with fillColor
|
||||
* @property {string} [options.fillColor] - A filling color for the shape
|
||||
* @property {number} [options.lineWidth=2] - The line width in pixels
|
||||
* @property {number} [options.opacity=1] - The opacity, between 0 (totally transparent) and 1 (totally opaque)
|
||||
* @property {string} [options.selectionColor='#00ff00'] - A selection color
|
||||
* @property {string} [options.hoverColor] - A hovered color
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents an ellipse shape
|
||||
*
|
||||
* @namespace
|
||||
* @typedef {Object} Ellipse
|
||||
*/
|
||||
// TODO : Ellipse, Circle and Footprint should inherit from the same root object
|
||||
export let Ellipse = (function() {
|
||||
/**
|
||||
* Constructor function for creating a new ellipse.
|
||||
*
|
||||
* @constructor
|
||||
* @memberof Ellipse
|
||||
* @param {number[]} center - right-ascension/declination 2-tuple of the ellipse's center in degrees
|
||||
* @param {number} a - semi-major axis length in degrees
|
||||
* @param {number} b - semi-minor axis length in degrees
|
||||
* @param {number} theta - angle of the ellipse in degrees
|
||||
* @param {ShapeOptions} options - Configuration options for the ellipse
|
||||
*
|
||||
* @returns {Ellipse} - The ellipse shape object
|
||||
*/
|
||||
let Ellipse = function(center, a, b, theta, options) {
|
||||
// constructor
|
||||
let Ellipse = function(centerRaDec, rayonXDegrees, rayonYDegrees, rotationDegrees, options) {
|
||||
options = options || {};
|
||||
|
||||
this.color = options['color'] || undefined;
|
||||
@@ -74,14 +43,13 @@ export let Ellipse = (function() {
|
||||
this.lineWidth = options["lineWidth"] || 2;
|
||||
this.selectionColor = options["selectionColor"] || '#00ff00';
|
||||
this.hoverColor = options["hoverColor"] || undefined;
|
||||
this.opacity = options['opacity'] || 1;
|
||||
|
||||
// TODO : all graphic overlays should have an id
|
||||
this.id = 'ellipse-' + Utils.uuidv4();
|
||||
|
||||
this.setCenter(center);
|
||||
this.setAxisLength(a, b);
|
||||
this.setRotation(theta);
|
||||
this.setCenter(centerRaDec);
|
||||
this.setRadiuses(rayonXDegrees, rayonYDegrees);
|
||||
this.setRotation(rotationDegrees);
|
||||
this.overlay = null;
|
||||
|
||||
this.isShowing = true;
|
||||
@@ -90,7 +58,7 @@ export let Ellipse = (function() {
|
||||
};
|
||||
|
||||
Ellipse.prototype.setColor = function(color) {
|
||||
if (!color || this.color == color) {
|
||||
if (this.color == color) {
|
||||
return;
|
||||
}
|
||||
this.color = color;
|
||||
@@ -100,7 +68,7 @@ export let Ellipse = (function() {
|
||||
};
|
||||
|
||||
Ellipse.prototype.setSelectionColor = function(color) {
|
||||
if (!color || this.selectionColor == color) {
|
||||
if (this.selectionColor == color) {
|
||||
return;
|
||||
}
|
||||
this.selectionColor = color;
|
||||
@@ -110,7 +78,7 @@ export let Ellipse = (function() {
|
||||
};
|
||||
|
||||
Ellipse.prototype.setHoverColor = function(color) {
|
||||
if (!color || this.hoverColor == color) {
|
||||
if (this.hoverColor == color) {
|
||||
return;
|
||||
}
|
||||
this.hoverColor = color;
|
||||
@@ -218,9 +186,9 @@ export let Ellipse = (function() {
|
||||
}
|
||||
};
|
||||
|
||||
Ellipse.prototype.setAxisLength = function(a, b) {
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
Ellipse.prototype.setRadiuses = function(radiusXDegrees, radiusYDegrees) {
|
||||
this.radiusXDegrees = radiusXDegrees;
|
||||
this.radiusYDegrees = radiusYDegrees;
|
||||
|
||||
if (this.overlay) {
|
||||
this.overlay.reportChange();
|
||||
@@ -232,58 +200,20 @@ export let Ellipse = (function() {
|
||||
}
|
||||
|
||||
// TODO
|
||||
Ellipse.prototype.draw = function(ctx, view, noStroke, noSmallCheck) {
|
||||
Ellipse.prototype.draw = function(ctx, view, noStroke) {
|
||||
if (! this.isShowing) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
const px_per_deg = view.width / view.fov;
|
||||
noSmallCheck = noSmallCheck===true || false;
|
||||
if (!noSmallCheck) {
|
||||
this.isTooSmall = this.b * 2 * px_per_deg < this.lineWidth;
|
||||
if (this.isTooSmall) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var originScreen = view.aladin.world2pix(this.centerRaDec[0], this.centerRaDec[1]);
|
||||
if (!originScreen) {
|
||||
var centerXyview = AladinUtils.radecToViewXy(this.centerRaDec[0], this.centerRaDec[1], view.aladin);
|
||||
if (!centerXyview) {
|
||||
// the center goes out of the projection
|
||||
// we do not draw it
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. Find the spherical tangent vector going to the north
|
||||
let toNorth = [this.centerRaDec[0], this.centerRaDec[1] + 1e-3];
|
||||
|
||||
// 2. Project it to the screen
|
||||
let toNorthScreen = view.aladin.world2pix(toNorth[0], toNorth[1]);
|
||||
|
||||
if(!toNorthScreen) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. normalize this vector
|
||||
let toNorthVec = [toNorthScreen[0] - originScreen[0], toNorthScreen[1] - originScreen[1]];
|
||||
let norm = Math.sqrt(toNorthVec[0]*toNorthVec[0] + toNorthVec[1]*toNorthVec[1]);
|
||||
|
||||
toNorthVec = [toNorthVec[0] / norm, toNorthVec[1] / norm];
|
||||
let toWestVec = [1.0, 0.0];
|
||||
|
||||
let x1 = toWestVec[0];
|
||||
let y1 = toWestVec[1];
|
||||
let x2 = toNorthVec[0];
|
||||
let y2 = toNorthVec[1];
|
||||
// 4. Compute the west to north angle
|
||||
let westToNorthAngle = Math.atan2(x1*y2-y1*x2, x1*x2+y1*y2);
|
||||
|
||||
// 5. Get the correct ellipse angle
|
||||
let theta = -this.rotation + westToNorthAngle;
|
||||
//let ct = Math.cos(theta);
|
||||
//let st = Math.sin(theta);
|
||||
|
||||
/*let circlePtXyViewRa = view.aladin.world2pix(view.viewCenter.lon + 1.0, view.viewCenter.lat);
|
||||
let circlePtXyViewDec = view.aladin.world2pix(view.viewCenter.lon, view.viewCenter.lat + 1.0);
|
||||
let circlePtXyViewRa = AladinUtils.radecToViewXy(this.centerRaDec[0] + this.radiusXDegrees, this.centerRaDec[1], view.aladin);
|
||||
let circlePtXyViewDec = AladinUtils.radecToViewXy(this.centerRaDec[0], this.centerRaDec[1] + this.radiusYDegrees, view.aladin);
|
||||
|
||||
if (!circlePtXyViewRa || !circlePtXyViewDec) {
|
||||
// the circle border goes out of the projection
|
||||
@@ -293,20 +223,17 @@ export let Ellipse = (function() {
|
||||
|
||||
var dxRa = circlePtXyViewRa[0] - centerXyview[0];
|
||||
var dyRa = circlePtXyViewRa[1] - centerXyview[1];
|
||||
var dRa = Math.sqrt(dxRa*dxRa + dyRa*dyRa);
|
||||
var radiusInPixX = Math.sqrt(dxRa*dxRa + dyRa*dyRa);
|
||||
|
||||
var dxDec = circlePtXyViewDec[0] - centerXyview[0];
|
||||
var dyDec = circlePtXyViewDec[1] - centerXyview[1];
|
||||
var dDec = Math.sqrt(dxDec*dxDec + dyDec*dyDec);*/
|
||||
|
||||
//var radiusInPixX = Math.abs(this.a * ct * dRa) + Math.abs(this.a * st * dDec);
|
||||
//var radiusInPixY = Math.abs(this.b * st * dRa) + Math.abs(this.b * ct * dDec);
|
||||
var radiusInPixY = Math.sqrt(dxDec*dxDec + dyDec*dyDec);
|
||||
|
||||
// Ellipse crossing the projection
|
||||
/*if ((dxRa*dyDec - dxDec*dyRa) <= 0.0) {
|
||||
if ((dxRa*dyDec - dxDec*dyRa) <= 0.0) {
|
||||
// We do not draw it
|
||||
return;
|
||||
}*/
|
||||
}
|
||||
noStroke = noStroke===true || false;
|
||||
|
||||
// TODO : check each 4 point until show
|
||||
@@ -330,11 +257,33 @@ export let Ellipse = (function() {
|
||||
ctx.strokeStyle = baseColor;
|
||||
}
|
||||
|
||||
ctx.lineWidth = this.lineWidth;
|
||||
ctx.globalAlpha = this.opacity;
|
||||
ctx.beginPath();
|
||||
// 1. Find the spherical tangent vector going to the north
|
||||
let origin = this.centerRaDec;
|
||||
let toNorth = [this.centerRaDec[0], this.centerRaDec[1] + 1e-3];
|
||||
|
||||
ctx.ellipse(originScreen[0], originScreen[1], px_per_deg * this.a, px_per_deg * this.b, theta, 0, 2*Math.PI, false);
|
||||
// 2. Project it to the screen
|
||||
let originScreen = this.overlay.view.wasm.worldToScreen(origin[0], origin[1]);
|
||||
let toNorthScreen = this.overlay.view.wasm.worldToScreen(toNorth[0], toNorth[1]);
|
||||
|
||||
// 3. normalize this vector
|
||||
let toNorthVec = [toNorthScreen[0] - originScreen[0], toNorthScreen[1] - originScreen[1]];
|
||||
let norm = Math.sqrt(toNorthVec[0]*toNorthVec[0] + toNorthVec[1]*toNorthVec[1]);
|
||||
|
||||
toNorthVec = [toNorthVec[0] / norm, toNorthVec[1] / norm];
|
||||
let toWestVec = [1.0, 0.0];
|
||||
|
||||
let x1 = toWestVec[0];
|
||||
let y1 = toWestVec[1];
|
||||
let x2 = toNorthVec[0];
|
||||
let y2 = toNorthVec[1];
|
||||
// 4. Compute the west to north angle
|
||||
let westToNorthAngle = Math.atan2(x1*y2-y1*x2, x1*x2+y1*y2);
|
||||
|
||||
// 5. Get the correct ellipse angle
|
||||
let theta = -this.rotation + westToNorthAngle;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.ellipse(centerXyview[0], centerXyview[1], radiusInPixX, radiusInPixY, theta, 0, 2*Math.PI, false);
|
||||
if (!noStroke) {
|
||||
if (this.fillColor) {
|
||||
ctx.fillStyle = this.fillColor;
|
||||
@@ -342,15 +291,10 @@ export let Ellipse = (function() {
|
||||
}
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
Ellipse.prototype.isInStroke = function(ctx, view, x, y) {
|
||||
if (!this.draw(ctx, view, true, true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.draw(ctx, view, true);
|
||||
return ctx.isPointInStroke(x, y);
|
||||
};
|
||||
|
||||
|
||||
@@ -56,6 +56,10 @@ export class CircleSelect extends FSM {
|
||||
let draw = () => {
|
||||
let ctx = view.catalogCtx;
|
||||
|
||||
if (!view.catalogCanvasCleared) {
|
||||
ctx.clearRect(0, 0, view.width, view.height);
|
||||
view.catalogCanvasCleared = true;
|
||||
}
|
||||
// draw the selection
|
||||
ctx.fillStyle = options.color + '7f';
|
||||
ctx.strokeStyle = options.color;
|
||||
|
||||
@@ -119,6 +119,10 @@ export class PolySelect extends FSM {
|
||||
let draw = () => {
|
||||
let ctx = view.catalogCtx;
|
||||
|
||||
if (!view.catalogCanvasCleared) {
|
||||
ctx.clearRect(0, 0, view.width, view.height);
|
||||
view.catalogCanvasCleared = true;
|
||||
}
|
||||
// draw the selection
|
||||
ctx.save();
|
||||
ctx.fillStyle = options.color + '7f';
|
||||
|
||||
@@ -57,6 +57,10 @@ export class RectSelect extends FSM {
|
||||
let draw = () => {
|
||||
let ctx = view.catalogCtx;
|
||||
|
||||
if (!view.catalogCanvasCleared) {
|
||||
ctx.clearRect(0, 0, view.width, view.height);
|
||||
view.catalogCanvasCleared = true;
|
||||
}
|
||||
// draw the selection
|
||||
ctx.fillStyle = options.color + '7f';
|
||||
ctx.strokeStyle = options.color;
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
import { AladinUtils } from './AladinUtils.js';
|
||||
import { Utils } from './Utils';
|
||||
|
||||
export let Footprint= (function() {
|
||||
@@ -42,11 +43,7 @@ export let Footprint= (function() {
|
||||
this.id = 'footprint-' + Utils.uuidv4();
|
||||
|
||||
this.source = source;
|
||||
if (this.source) {
|
||||
this.source.hasFootprint = true;
|
||||
}
|
||||
|
||||
this.shapes = [].concat(shapes);
|
||||
this.shapes = shapes;
|
||||
|
||||
this.isShowing = true;
|
||||
this.isHovered = false;
|
||||
@@ -57,16 +54,6 @@ export let Footprint= (function() {
|
||||
Footprint.prototype.setCatalog = function(catalog) {
|
||||
if (this.source) {
|
||||
this.source.setCatalog(catalog);
|
||||
|
||||
for (var s of this.shapes) {
|
||||
if (!s.color) {
|
||||
s.setColor(catalog.color);
|
||||
}
|
||||
|
||||
// Selection and hover color are catalog options
|
||||
s.setSelectionColor(catalog.selectionColor);
|
||||
s.setHoverColor(catalog.hoverColor);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -106,12 +93,8 @@ export let Footprint= (function() {
|
||||
|
||||
if (this.overlay) {
|
||||
this.overlay.reportChange();
|
||||
return;
|
||||
}
|
||||
|
||||
let catalog = this.getCatalog();
|
||||
if (catalog) {
|
||||
catalog.view && catalog.view.requestRedraw();
|
||||
} else if (this.source && this.source.catalog) {
|
||||
this.source.catalog.view && this.source.catalog.view.requestRedraw();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -125,43 +108,30 @@ export let Footprint= (function() {
|
||||
|
||||
if (this.overlay) {
|
||||
this.overlay.reportChange();
|
||||
}
|
||||
|
||||
let catalog = this.getCatalog();
|
||||
if (catalog) {
|
||||
catalog.view && catalog.view.requestRedraw();
|
||||
} else if (this.source && this.source.catalog) {
|
||||
this.source.catalog.view && this.source.catalog.view.requestRedraw();
|
||||
}
|
||||
};
|
||||
|
||||
Footprint.prototype.getLineWidth = function() {
|
||||
return this.shapes && this.shapes[0].getLineWidth();
|
||||
};
|
||||
|
||||
|
||||
Footprint.prototype.setLineWidth = function(lineWidth) {
|
||||
this.shapes.forEach((shape) => shape.setLineWidth(lineWidth))
|
||||
};
|
||||
|
||||
Footprint.prototype.setColor = function(color) {
|
||||
if(!color) {
|
||||
return;
|
||||
Footprint.prototype.getLineWidth = function() {
|
||||
if (this.shapes && this.shapes.length > 0) {
|
||||
return this.shapes[0].getLineWidth();
|
||||
}
|
||||
};
|
||||
|
||||
Footprint.prototype.setColor = function(color) {
|
||||
this.shapes.forEach((shape) => shape.setColor(color))
|
||||
};
|
||||
|
||||
Footprint.prototype.setSelectionColor = function(color) {
|
||||
if (!color) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.shapes.forEach((shape) => shape.setSelectionColor(color))
|
||||
};
|
||||
|
||||
Footprint.prototype.setHoverColor = function(color) {
|
||||
if (!color)
|
||||
return;
|
||||
|
||||
this.shapes.forEach((shape) => shape.setHoverColor(color))
|
||||
};
|
||||
|
||||
@@ -170,7 +140,7 @@ export let Footprint= (function() {
|
||||
}
|
||||
|
||||
Footprint.prototype.draw = function(ctx, view, noStroke) {
|
||||
return this.shapes.some((shape) => {return shape.draw(ctx, view, noStroke)})
|
||||
this.shapes.forEach((shape) => shape.draw(ctx, view, noStroke))
|
||||
};
|
||||
|
||||
Footprint.prototype.actionClicked = function() {
|
||||
@@ -192,10 +162,6 @@ export let Footprint= (function() {
|
||||
return this.shapes.some((shape) => shape.isInStroke(ctx, view, x, y));
|
||||
};
|
||||
|
||||
Footprint.prototype.isTooSmall = function(view) {
|
||||
return this.shapes.every((shape) => shape.isTooSmall);
|
||||
};
|
||||
|
||||
Footprint.prototype.getCatalog = function() {
|
||||
return this.source && this.source.catalog;
|
||||
};
|
||||
@@ -219,7 +185,7 @@ export let Footprint= (function() {
|
||||
y: s.y,
|
||||
};
|
||||
} else {
|
||||
var xy = view.aladin.world2pix(s.ra, s.dec);
|
||||
var xy = AladinUtils.radecToViewXy(s.ra, s.dec, view.aladin);
|
||||
if (!xy) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ HiPSProperties.fetchFromID = async function(ID) {
|
||||
result = matching;
|
||||
} else {
|
||||
result = metadata[0];
|
||||
console.warn("Multiple surveys are matching, please choose one. The chosen one is: " + result.ID);
|
||||
console.warn("Multiple surveys are matching, please choose one. The chosen one is: " + result);
|
||||
}
|
||||
} else {
|
||||
// Exactly one matching
|
||||
@@ -214,15 +214,14 @@ HiPSProperties.getFasterMirrorUrl = function (metadata, currUrl) {
|
||||
newUrlResp = validResponses[0];
|
||||
} else {
|
||||
// no valid response => we return an error
|
||||
return Promise.reject('All mirrors urls have been tested:' + urls)
|
||||
return Promise.reject('Survey not found. All mirrors urls have been tested:' + urls)
|
||||
}
|
||||
|
||||
// check if there is a big difference from the current one
|
||||
let currUrlResp = validResponses.find((r) => r.baseUrl === currUrl)
|
||||
// it may happen that the url requested by the user is too slow hence discarded
|
||||
// for these cases, we automatically switch to the new fastest url.
|
||||
|
||||
let urlChosen;
|
||||
if (currUrlResp && Math.abs(currUrlResp.duration - newUrlResp.duration) / Math.min(currUrlResp.duration, newUrlResp.duration) < 0.10) {
|
||||
if (Math.abs(currUrlResp.duration - newUrlResp.duration) / Math.min(currUrlResp.duration, newUrlResp.duration) < 0.10) {
|
||||
// there is not enough difference => do not switch
|
||||
urlChosen = currUrlResp.baseUrl;
|
||||
} else {
|
||||
|
||||
@@ -17,63 +17,38 @@
|
||||
// along with Aladin Lite.
|
||||
//
|
||||
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Aladin Lite project
|
||||
*
|
||||
*
|
||||
* File ImageFITS
|
||||
*
|
||||
*
|
||||
* Authors: Matthieu Baumann [CDS]
|
||||
*
|
||||
*
|
||||
*****************************************************************************/
|
||||
import { ALEvent } from "./events/ALEvent.js";
|
||||
import { ColorCfg } from "./ColorCfg.js";
|
||||
import { Utils } from "./Utils";
|
||||
import { HiPSCache } from "./DefaultHiPSCache";
|
||||
|
||||
/**
|
||||
* @typedef {Object} ImageFITSOptions
|
||||
*
|
||||
* @property {string} [name] - A human-readable name for the FITS image
|
||||
* @property {Function} [successCallback] - A callback executed when the FITS has been loaded
|
||||
* @property {Function} [errorCallback] - A callback executed when the FITS could not be loaded
|
||||
* @property {number} [opacity=1.0] - Opacity of the survey or image (value between 0 and 1).
|
||||
* @property {string} [colormap="native"] - The colormap configuration for the survey or image.
|
||||
* @property {string} [stretch="linear"] - The stretch configuration for the survey or image.
|
||||
* @property {boolean} [reversed=false] - If true, the colormap is reversed; otherwise, it is not reversed.
|
||||
* @property {number} [minCut] - The minimum cut value for the color configuration. If not given, 0.0 for JPEG/PNG surveys, the value of the property file for FITS surveys
|
||||
* @property {number} [maxCut] - The maximum cut value for the color configuration. If not given, 1.0 for JPEG/PNG surveys, the value of the property file for FITS surveys
|
||||
* @property {boolean} [additive=false] - If true, additive blending is applied; otherwise, it is not applied.
|
||||
* @property {number} [gamma=1.0] - The gamma correction value for the color configuration.
|
||||
* @property {number} [saturation=0.0] - The saturation value for the color configuration.
|
||||
* @property {number} [brightness=0.0] - The brightness value for the color configuration.
|
||||
* @property {number} [contrast=0.0] - The contrast value for the color configuration.
|
||||
*/
|
||||
|
||||
export let ImageFITS = (function () {
|
||||
/**
|
||||
* The object describing a FITS image
|
||||
*
|
||||
* @class
|
||||
* @constructs ImageFITS
|
||||
*
|
||||
* @param {string} url - Mandatory unique identifier for the layer. Can be an arbitrary name
|
||||
* @param {ImageFITSOptions} [options] - The option for the survey
|
||||
*
|
||||
*/
|
||||
function ImageFITS(url, options) {
|
||||
|
||||
function ImageFITS(url, name, options, successCallback = undefined, errorCallback = undefined) {
|
||||
// Name of the layer
|
||||
this.layer = null;
|
||||
this.added = false;
|
||||
this.subtype = "fits";
|
||||
// Set it to a default value
|
||||
this.url = url;
|
||||
this.id = url;
|
||||
this.name = (options && options.name) || this.url;
|
||||
this.url = url.toString();
|
||||
|
||||
this.id = url.toString();
|
||||
this.name = name || this.url;
|
||||
|
||||
this.imgFormat = "fits";
|
||||
this.formats = ["fits"];
|
||||
this.formats = ["fits"]
|
||||
// callbacks
|
||||
this.successCallback = options && options.successCallback;
|
||||
this.errorCallback = options && options.errorCallback;
|
||||
this.successCallback = successCallback;
|
||||
this.errorCallback = errorCallback;
|
||||
// initialize the color meta data here
|
||||
// set a asinh stretch by default if there is none
|
||||
/*if (options) {
|
||||
@@ -85,19 +60,13 @@ export let ImageFITS = (function () {
|
||||
|
||||
this.query = Promise.resolve(self);
|
||||
}
|
||||
|
||||
ImageFITS.prototype._saveInCache = function () {
|
||||
HiPSCache.append(this.id, this);
|
||||
};
|
||||
|
||||
|
||||
// A cache storing directly the images to not query for the properties each time
|
||||
//ImageFITS.cache = {};
|
||||
ImageFITS.cache = {};
|
||||
|
||||
ImageFITS.prototype.setView = function (view) {
|
||||
ImageFITS.prototype.setView = function(view) {
|
||||
this.view = view;
|
||||
|
||||
this._saveInCache();
|
||||
};
|
||||
}
|
||||
|
||||
// @api
|
||||
ImageFITS.prototype.setOpacity = function (opacity) {
|
||||
@@ -119,7 +88,7 @@ export let ImageFITS = (function () {
|
||||
this._updateMetadata(() => {
|
||||
this.colorCfg.setColormap(colormap, options);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// @api
|
||||
ImageFITS.prototype.setCuts = function (lowCut, highCut) {
|
||||
@@ -166,101 +135,92 @@ export let ImageFITS = (function () {
|
||||
this.view.wasm.setImageMetadata(this.layer, {
|
||||
...this.colorCfg.get(),
|
||||
longitudeReversed: false,
|
||||
imgFormat: this.imgFormat,
|
||||
});
|
||||
ALEvent.HIPS_LAYER_CHANGED.dispatchedTo(this.view.aladinDiv, {
|
||||
layer: this,
|
||||
imgFormat: this.imgFormat
|
||||
});
|
||||
ALEvent.HIPS_LAYER_CHANGED.dispatchedTo(this.view.aladinDiv, { layer: this });
|
||||
}
|
||||
|
||||
// save it in the JS HiPS cache
|
||||
this._saveInCache();
|
||||
} catch (e) {
|
||||
// Display the error message
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
ImageFITS.prototype.add = function (layer) {
|
||||
this.layer = layer;
|
||||
|
||||
let self = this;
|
||||
|
||||
const promise = self.view.wasm
|
||||
.addImageFITS({
|
||||
layer: self.layer,
|
||||
url: self.url,
|
||||
meta: {
|
||||
...this.colorCfg.get(),
|
||||
longitudeReversed: false,
|
||||
imgFormat: this.imgFormat,
|
||||
},
|
||||
})
|
||||
.then((imagesParams) => {
|
||||
// There is at least one entry in imageParams
|
||||
self.added = true;
|
||||
const promise = self.view.wasm.addImageFITS({
|
||||
layer: self.layer,
|
||||
url: self.url,
|
||||
meta: {
|
||||
...this.colorCfg.get(),
|
||||
longitudeReversed: false,
|
||||
imgFormat: this.imgFormat
|
||||
}
|
||||
}).then((imagesParams) => {
|
||||
// There is at least one entry in imageParams
|
||||
self.added = true;
|
||||
|
||||
self.children = [];
|
||||
self.children = [];
|
||||
|
||||
let hduIdx = 0;
|
||||
imagesParams.forEach((imageParams) => {
|
||||
// This fits has HDU extensions
|
||||
let image = new ImageFITS(imageParams.url, {
|
||||
name: self.name + "_ext_" + hduIdx.toString(),
|
||||
});
|
||||
let hduIdx = 0;
|
||||
imagesParams.forEach((imageParams) => {
|
||||
// This fits has HDU extensions
|
||||
let image = new ImageFITS(
|
||||
imageParams.url,
|
||||
self.name + "_ext_" + hduIdx.toString(),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
// Set the layer corresponding to the onein the backend
|
||||
image.layer = imageParams.layer;
|
||||
image.added = true;
|
||||
image.setView(self.view);
|
||||
// deep copy of the color object of self
|
||||
image.colorCfg = Utils.deepCopy(self.colorCfg);
|
||||
// Set the automatic computed cuts
|
||||
image.setCuts(
|
||||
imageParams.automatic_min_cut,
|
||||
imageParams.automatic_max_cut
|
||||
);
|
||||
// Set the layer corresponding to the onein the backend
|
||||
image.layer = imageParams.layer;
|
||||
image.added = true;
|
||||
image.setView(self.view);
|
||||
// deep copy of the color object of self
|
||||
image.colorCfg = Utils.deepCopy(self.colorCfg);
|
||||
// Set the automatic computed cuts
|
||||
image.setCuts(imageParams.automatic_min_cut, imageParams.automatic_max_cut);
|
||||
|
||||
image.ra = imageParams.centered_fov.ra;
|
||||
image.dec = imageParams.centered_fov.dec;
|
||||
image.fov = imageParams.centered_fov.fov;
|
||||
image.ra = imageParams.centered_fov.ra;
|
||||
image.dec = imageParams.centered_fov.dec;
|
||||
image.fov = imageParams.centered_fov.fov;
|
||||
|
||||
if (!self.ra) {
|
||||
self.ra = image.ra;
|
||||
}
|
||||
if (!self.dec) {
|
||||
self.dec = image.dec;
|
||||
}
|
||||
if (!self.fov) {
|
||||
self.fov = image.fov;
|
||||
}
|
||||
if (!self.ra) { self.ra = image.ra; }
|
||||
if (!self.dec) { self.dec = image.dec; }
|
||||
if (!self.fov) { self.fov = image.fov; }
|
||||
|
||||
self.children.push(image);
|
||||
self.children.push(image)
|
||||
|
||||
hduIdx += 1;
|
||||
});
|
||||
|
||||
// Call the success callback on the first HDU image parsed
|
||||
if (self.successCallback) {
|
||||
self.successCallback(
|
||||
self.children[0].ra,
|
||||
self.children[0].dec,
|
||||
self.children[0].fov,
|
||||
self.children[0]
|
||||
);
|
||||
}
|
||||
|
||||
return self;
|
||||
})
|
||||
.catch((e) => {
|
||||
// This error result from a promise
|
||||
// If I throw it, it will not be catched because
|
||||
// it is run async
|
||||
self.view.removeImageLayer(layer);
|
||||
|
||||
return Promise.reject(e);
|
||||
hduIdx += 1;
|
||||
});
|
||||
|
||||
// Call the success callback on the first HDU image parsed
|
||||
if (self.successCallback) {
|
||||
self.successCallback(
|
||||
self.children[0].ra,
|
||||
self.children[0].dec,
|
||||
self.children[0].fov,
|
||||
self.children[0]
|
||||
);
|
||||
}
|
||||
|
||||
return self;
|
||||
}).catch((e) => {
|
||||
if (self.errorCallback) {
|
||||
self.errorCallback()
|
||||
}
|
||||
|
||||
// This error result from a promise
|
||||
// If I throw it, it will not be catched because
|
||||
// it is run async
|
||||
self.view.removeImageLayer(layer)
|
||||
|
||||
return Promise.reject(e);
|
||||
});
|
||||
|
||||
return promise;
|
||||
};
|
||||
|
||||
@@ -274,9 +234,9 @@ export let ImageFITS = (function () {
|
||||
};
|
||||
|
||||
// FITS images does not mean to be used for storing planetary data
|
||||
ImageFITS.prototype.isPlanetaryBody = function () {
|
||||
ImageFITS.prototype.isPlanetaryBody = function() {
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
// @api
|
||||
ImageFITS.prototype.focusOn = function () {
|
||||
@@ -320,3 +280,4 @@ export let ImageFITS = (function () {
|
||||
|
||||
return ImageFITS;
|
||||
})();
|
||||
|
||||
|
||||
@@ -1,861 +0,0 @@
|
||||
// Copyright 2013 - UDS/CNRS
|
||||
// The Aladin Lite program is distributed under the terms
|
||||
// of the GNU General Public License version 3.
|
||||
//
|
||||
// This file is part of Aladin Lite.
|
||||
//
|
||||
// Aladin Lite is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 3 of the License.
|
||||
//
|
||||
// Aladin Lite is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// The GNU General Public License is available in COPYING file
|
||||
// along with Aladin Lite.
|
||||
//
|
||||
|
||||
/******************************************************************************
|
||||
* Aladin Lite project
|
||||
*
|
||||
* File ImageHiPS
|
||||
*
|
||||
* Authors: Thomas Boch & Matthieu Baumann [CDS]
|
||||
*
|
||||
*****************************************************************************/
|
||||
import { Utils } from "./Utils";
|
||||
import { ALEvent } from "./events/ALEvent.js";
|
||||
import { ColorCfg } from "./ColorCfg.js";
|
||||
import { HiPSProperties } from "./HiPSProperties.js";
|
||||
import { HiPSCache } from "./DefaultHiPSCache.js";
|
||||
|
||||
let PropertyParser = {};
|
||||
// Utilitary functions for parsing the properties and giving default values
|
||||
/// Mandatory tileSize property
|
||||
PropertyParser.tileSize = function (properties) {
|
||||
let tileSize =
|
||||
(properties &&
|
||||
properties.hips_tile_width &&
|
||||
+properties.hips_tile_width) ||
|
||||
512;
|
||||
|
||||
// Check if the tile width size is a power of 2
|
||||
if (tileSize & (tileSize - 1 !== 0)) {
|
||||
tileSize = 512;
|
||||
}
|
||||
|
||||
return tileSize;
|
||||
};
|
||||
|
||||
/// Mandatory frame property
|
||||
PropertyParser.cooFrame = function (properties) {
|
||||
let cooFrame =
|
||||
(properties && properties.hips_body && "ICRSd") ||
|
||||
(properties && properties.hips_frame) ||
|
||||
"ICRS";
|
||||
return cooFrame;
|
||||
};
|
||||
|
||||
/// Mandatory maxOrder property
|
||||
PropertyParser.maxOrder = function (properties) {
|
||||
let maxOrder =
|
||||
properties && properties.hips_order && +properties.hips_order;
|
||||
return maxOrder;
|
||||
};
|
||||
|
||||
/// Mandatory minOrder property
|
||||
PropertyParser.minOrder = function (properties) {
|
||||
const minOrder =
|
||||
(properties &&
|
||||
properties.hips_order_min &&
|
||||
+properties.hips_order_min) ||
|
||||
0;
|
||||
return minOrder;
|
||||
};
|
||||
|
||||
PropertyParser.formats = function (properties) {
|
||||
let formats = (properties && properties.hips_tile_format) || "jpeg";
|
||||
|
||||
formats = formats.split(" ").map((fmt) => fmt.toLowerCase());
|
||||
|
||||
return formats;
|
||||
};
|
||||
|
||||
PropertyParser.initialFov = function (properties) {
|
||||
let initialFov =
|
||||
properties &&
|
||||
properties.hips_initial_fov &&
|
||||
+properties.hips_initial_fov;
|
||||
|
||||
if (initialFov && initialFov < 0.00002777777) {
|
||||
initialFov = 360;
|
||||
}
|
||||
|
||||
return initialFov;
|
||||
};
|
||||
|
||||
PropertyParser.skyFraction = function (properties) {
|
||||
const skyFraction =
|
||||
(properties &&
|
||||
properties.moc_sky_fraction &&
|
||||
+properties.moc_sky_fraction) ||
|
||||
0.0;
|
||||
return skyFraction;
|
||||
};
|
||||
|
||||
PropertyParser.cutouts = function (properties) {
|
||||
let cuts =
|
||||
properties &&
|
||||
properties.hips_pixel_cut &&
|
||||
properties.hips_pixel_cut.split(" ");
|
||||
|
||||
const minCutout = cuts && parseFloat(cuts[0]);
|
||||
const maxCutout = cuts && parseFloat(cuts[1]);
|
||||
|
||||
return [minCutout, maxCutout];
|
||||
};
|
||||
|
||||
PropertyParser.bitpix = function (properties) {
|
||||
const bitpix =
|
||||
properties &&
|
||||
properties.hips_pixel_bitpix &&
|
||||
+properties.hips_pixel_bitpix;
|
||||
return bitpix;
|
||||
};
|
||||
|
||||
PropertyParser.isPlanetaryBody = function (properties) {
|
||||
return properties && properties.hips_body !== undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {Object} ImageHiPSOptions
|
||||
*
|
||||
* @property {string} [name] - The name of the survey to be displayed in the UI
|
||||
* @property {Function} [successCallback] - A callback executed when the HiPS has been loaded
|
||||
* @property {Function} [errorCallback] - A callback executed when the HiPS could not be loaded
|
||||
* @property {string} [imgFormat] - Formats accepted 'webp', 'png', 'jpeg' or 'fits'. Will raise an error if the HiPS does not contain tiles in this format
|
||||
* @property {CooFrame} [cooFrame="J2000"] - Coordinate frame of the survey tiles
|
||||
* @property {number} [maxOrder] - The maximum HEALPix order of the HiPS, i.e the HEALPix order of the most refined tile images of the HiPS.
|
||||
* @property {number} [numBitsPerPixel] - Useful if you want to display the FITS tiles of a HiPS. It specifies the number of bits per pixel. Possible values are:
|
||||
* -64: double, -32: float, 8: unsigned byte, 16: short, 32: integer 32 bits, 64: integer 64 bits
|
||||
* @property {number} [tileSize] - The width of the HEALPix tile images. Mostly 512 pixels but can be 256, 128, 64, 32
|
||||
* @property {number} [minOrder] - If not given, retrieved from the properties of the survey.
|
||||
* @property {boolean} [longitudeReversed=false] - Set it to True for planetary survey visualization
|
||||
* @property {number} [opacity=1.0] - Opacity of the survey or image (value between 0 and 1).
|
||||
* @property {string} [colormap="native"] - The colormap configuration for the survey or image.
|
||||
* @property {string} [stretch="linear"] - The stretch configuration for the survey or image.
|
||||
* @property {boolean} [reversed=false] - If true, the colormap is reversed; otherwise, it is not reversed.
|
||||
* @property {number} [minCut] - The minimum cut value for the color configuration. If not given, 0.0 for JPEG/PNG surveys, the value of the property file for FITS surveys
|
||||
* @property {number} [maxCut] - The maximum cut value for the color configuration. If not given, 1.0 for JPEG/PNG surveys, the value of the property file for FITS surveys
|
||||
* @property {boolean} [additive=false] - If true, additive blending is applied; otherwise, it is not applied.
|
||||
* @property {number} [gamma=1.0] - The gamma correction value for the color configuration.
|
||||
* @property {number} [saturation=0.0] - The saturation value for the color configuration.
|
||||
* @property {number} [brightness=0.0] - The brightness value for the color configuration.
|
||||
* @property {number} [contrast=0.0] - The contrast value for the color configuration.
|
||||
*/
|
||||
export let ImageHiPS = (function () {
|
||||
/**
|
||||
* The object describing an image survey
|
||||
*
|
||||
* @class
|
||||
* @constructs ImageHiPS
|
||||
*
|
||||
* @param {string} id - Mandatory unique identifier for the layer. Can be an arbitrary name
|
||||
* @param {string} url - Can be an url to the survey or a "CDS" ID pointing towards a HiPS. One can found the list of IDs {@link https://aladin.cds.unistra.fr/hips/list| here}
|
||||
* @param {ImageHiPSOptions} [options] - The option for the survey
|
||||
*
|
||||
* @description Giving a CDS ID will do a query to the MOCServer first to retrieve metadata. Then it will also check for the presence of faster HiPS nodes to choose a faster url to query to tiles from.
|
||||
*/
|
||||
function ImageHiPS(id, url, options) {
|
||||
this.added = false;
|
||||
// Unique identifier for a survey
|
||||
this.id = id;
|
||||
this.name = (options && options.name) || undefined;
|
||||
this.url = url;
|
||||
this.maxOrder = options.maxOrder;
|
||||
this.minOrder = options.minOrder || 0;
|
||||
this.cooFrame = options.cooFrame;
|
||||
this.tileSize = options.tileSize;
|
||||
this.skyFraction = options.skyFraction;
|
||||
this.longitudeReversed =
|
||||
options.longitudeReversed === undefined
|
||||
? false
|
||||
: options.longitudeReversed;
|
||||
this.imgFormat = options.imgFormat;
|
||||
this.numBitsPerPixel = options.numBitsPerPixel;
|
||||
this.creatorDid = options.creatorDid;
|
||||
this.errorCallback = options.errorCallback;
|
||||
this.successCallback = options.successCallback;
|
||||
|
||||
this.colorCfg = new ColorCfg(options);
|
||||
}
|
||||
|
||||
ImageHiPS.prototype.setView = function (view) {
|
||||
let self = this;
|
||||
|
||||
// do not allow to call setView multiple times otherwise
|
||||
// the querying to the properties and the search to the best
|
||||
// HiPS node will be done again for the same imageHiPS
|
||||
if (self.view) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.view = view;
|
||||
|
||||
let isMOCServerToBeQueried = true;
|
||||
if (this.imgFormat === "fits") {
|
||||
// a fits is given
|
||||
isMOCServerToBeQueried = !(
|
||||
this.maxOrder &&
|
||||
this.url &&
|
||||
this.imgFormat &&
|
||||
this.tileSize &&
|
||||
this.cooFrame &&
|
||||
this.numBitsPerPixel
|
||||
);
|
||||
} else {
|
||||
isMOCServerToBeQueried = !(
|
||||
this.maxOrder &&
|
||||
this.url &&
|
||||
this.imgFormat &&
|
||||
this.tileSize &&
|
||||
this.cooFrame
|
||||
);
|
||||
}
|
||||
|
||||
self.query = (async () => {
|
||||
if (isMOCServerToBeQueried) {
|
||||
let isCDSId = false;
|
||||
|
||||
let properties = await HiPSProperties.fetchFromUrl(self.url)
|
||||
/*.catch((e) => {
|
||||
// try with the proxy
|
||||
url = Utils.handleCORSNotSameOrigin(url).href;
|
||||
|
||||
return HiPSProperties.fetchFromUrl(url);
|
||||
})*/
|
||||
.catch(async (e) => {
|
||||
// url not valid so we try with the id
|
||||
try {
|
||||
isCDSId = true;
|
||||
// the url stores a "CDS ID" we take it prioritaly
|
||||
// if the url is null, take the id, this is for some tests
|
||||
// to pass because some users might just give null as url param and a "CDS ID" as id param
|
||||
let id = self.url || self.id;
|
||||
return await HiPSProperties.fetchFromID(id);
|
||||
} catch (e) {
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
|
||||
//obsTitle = properties.obs_title;
|
||||
self.creatorDid = properties.creator_did || self.creatorDid;
|
||||
// url
|
||||
|
||||
if (isCDSId) {
|
||||
self.url = properties.hips_service_url;
|
||||
if (!self.url) {
|
||||
throw "no valid service URL for retrieving the tiles";
|
||||
}
|
||||
|
||||
self.url = Utils.fixURLForHTTPS(self.url);
|
||||
|
||||
// Request all the properties to see which mirror is the fastest
|
||||
HiPSProperties.getFasterMirrorUrl(properties, self.url)
|
||||
.then((url) => {
|
||||
if (self.url !== url) {
|
||||
console.info(
|
||||
"Change url of ",
|
||||
self.id,
|
||||
" from ",
|
||||
self.url,
|
||||
" to ",
|
||||
url
|
||||
);
|
||||
|
||||
self.url = url;
|
||||
|
||||
// save the new url to the cache
|
||||
self._saveInCache();
|
||||
|
||||
// If added to the backend, then we need to tell it the url has changed
|
||||
if (self.added) {
|
||||
self.view.wasm.setHiPSUrl(
|
||||
self.creatorDid,
|
||||
url
|
||||
);
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(self);
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
|
||||
// Max order
|
||||
self.maxOrder =
|
||||
PropertyParser.maxOrder(properties) || self.maxOrder;
|
||||
|
||||
// Tile size
|
||||
self.tileSize =
|
||||
PropertyParser.tileSize(properties) || self.tileSize;
|
||||
|
||||
// Tile formats
|
||||
self.formats =
|
||||
PropertyParser.formats(properties) || self.formats;
|
||||
|
||||
// min order
|
||||
self.minOrder =
|
||||
PropertyParser.minOrder(properties) || self.minOrder;
|
||||
|
||||
// Frame
|
||||
self.cooFrame =
|
||||
PropertyParser.cooFrame(properties) || self.cooFrame;
|
||||
|
||||
// sky fraction
|
||||
self.skyFraction = PropertyParser.skyFraction(properties);
|
||||
|
||||
// Initial fov/ra/dec
|
||||
self.initialFov = PropertyParser.initialFov(properties);
|
||||
self.initialRa =
|
||||
properties &&
|
||||
properties.hips_initial_ra &&
|
||||
+properties.hips_initial_ra;
|
||||
self.initialDec =
|
||||
properties &&
|
||||
properties.hips_initial_dec &&
|
||||
+properties.hips_initial_dec;
|
||||
|
||||
// Cutouts
|
||||
const cutoutFromProperties = PropertyParser.cutouts(properties);
|
||||
self.minCut = cutoutFromProperties[0];
|
||||
self.maxCut = cutoutFromProperties[1];
|
||||
|
||||
// Bitpix
|
||||
self.numBitsPerPixel =
|
||||
PropertyParser.bitpix(properties) || self.numBitsPerPixel;
|
||||
|
||||
// HiPS body
|
||||
if (properties.hips_body) {
|
||||
self.hipsBody = properties.hips_body;
|
||||
// Use the property to define and check some user given infos
|
||||
// Longitude reversed
|
||||
self.longitudeReversed = true;
|
||||
}
|
||||
|
||||
// Give a better name if we have the HiPS metadata
|
||||
self.name = self.name || properties.obs_title;
|
||||
}
|
||||
|
||||
self.name = self.name || self.id || self.url;
|
||||
self.name = self.name.replace(/ +/g, ' ');
|
||||
|
||||
self.creatorDid = self.creatorDid || self.id || self.url;
|
||||
|
||||
// Image format
|
||||
if (self.imgFormat) {
|
||||
// transform to lower case
|
||||
self.imgFormat = self.imgFormat.toLowerCase();
|
||||
// convert JPG -> JPEG
|
||||
if (self.imgFormat === "jpg") {
|
||||
self.imgFormat = "jpeg";
|
||||
}
|
||||
|
||||
// user wants a fits but the properties tells this format is not available
|
||||
if (
|
||||
self.imgFormat === "fits" &&
|
||||
self.formats &&
|
||||
self.formats.indexOf("fits") < 0
|
||||
) {
|
||||
throw self.name + " does not provide fits tiles";
|
||||
}
|
||||
|
||||
if (
|
||||
self.imgFormat === "webp" &&
|
||||
self.formats &&
|
||||
self.formats.indexOf("webp") < 0
|
||||
) {
|
||||
throw self.name + " does not provide webp tiles";
|
||||
}
|
||||
|
||||
if (
|
||||
self.imgFormat === "png" &&
|
||||
self.formats &&
|
||||
self.formats.indexOf("png") < 0
|
||||
) {
|
||||
throw self.name + " does not provide png tiles";
|
||||
}
|
||||
|
||||
if (
|
||||
self.imgFormat === "jpeg" &&
|
||||
self.formats &&
|
||||
self.formats.indexOf("jpeg") < 0
|
||||
) {
|
||||
throw self.name + " does not provide jpeg tiles";
|
||||
}
|
||||
} else {
|
||||
// user wants nothing then we choose one from the properties
|
||||
if (self.formats.indexOf("webp") >= 0) {
|
||||
self.imgFormat = "webp";
|
||||
} else if (self.formats.indexOf("png") >= 0) {
|
||||
self.imgFormat = "png";
|
||||
} else if (self.formats.indexOf("jpeg") >= 0) {
|
||||
self.imgFormat = "jpeg";
|
||||
} else if (self.formats.indexOf("fits") >= 0) {
|
||||
self.imgFormat = "fits";
|
||||
} else {
|
||||
throw (
|
||||
"Unsupported format(s) found in the properties: " +
|
||||
self.formats
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Cutouts
|
||||
let minCut, maxCut;
|
||||
if (self.imgFormat === "fits") {
|
||||
// Take into account the default cuts given by the property file (this is true especially for FITS HiPSes)
|
||||
minCut = self.colorCfg.minCut || self.minCut || 0.0;
|
||||
maxCut = self.colorCfg.maxCut || self.maxCut || 1.0;
|
||||
} else {
|
||||
minCut = self.colorCfg.minCut || 0.0;
|
||||
maxCut = self.colorCfg.maxCut || 1.0;
|
||||
}
|
||||
|
||||
self.colorCfg.setCuts(minCut, maxCut);
|
||||
|
||||
// Coo frame
|
||||
if (
|
||||
self.cooFrame == "ICRS" ||
|
||||
self.cooFrame == "ICRSd" ||
|
||||
self.cooFrame == "equatorial" ||
|
||||
self.cooFrame == "j2000"
|
||||
) {
|
||||
self.cooFrame = "ICRS";
|
||||
} else if (self.cooFrame == "galactic") {
|
||||
self.cooFrame = "GAL";
|
||||
} else {
|
||||
self.cooFrame = "ICRS";
|
||||
console.warn(
|
||||
"Invalid cooframe given: " +
|
||||
self.cooFrame +
|
||||
'. Coordinate systems supported: "ICRS", "ICRSd", "j2000" or "galactic". ICRS is chosen by default'
|
||||
);
|
||||
}
|
||||
|
||||
self.formats = self.formats || [self.imgFormat];
|
||||
|
||||
self._saveInCache();
|
||||
|
||||
return self;
|
||||
})()
|
||||
};
|
||||
|
||||
ImageHiPS.prototype._saveInCache = function () {
|
||||
let self = this;
|
||||
|
||||
let colorOpt = Object.fromEntries(Object.entries(this.colorCfg));
|
||||
let surveyOpt = {
|
||||
id: self.id,
|
||||
creatorDid: self.creatorDid,
|
||||
name: self.name,
|
||||
url: self.url,
|
||||
skyFraction: self.skyFraction,
|
||||
cooFrame: self.cooFrame,
|
||||
maxOrder: self.maxOrder,
|
||||
tileSize: self.tileSize,
|
||||
imgFormat: self.imgFormat,
|
||||
successCallback: self.successCallback,
|
||||
errorCallback: self.errorCallback,
|
||||
...colorOpt,
|
||||
};
|
||||
|
||||
if (self.numBitsPerPixel) {
|
||||
surveyOpt.numBitsPerPixel = self.numBitsPerPixel;
|
||||
}
|
||||
|
||||
HiPSCache.append(self.id, {
|
||||
// Erase by the cache already put values which is considered
|
||||
// as the ground truth
|
||||
...HiPSCache.get[self.id],
|
||||
// append new important infos from the properties queried
|
||||
...surveyOpt,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if the ImageHiPS represents a planetary body.
|
||||
*
|
||||
* This method returns a boolean indicating whether the ImageHiPS corresponds to a planetary body, e.g. the earth or a celestial body.
|
||||
*
|
||||
* @memberof ImageHiPS
|
||||
*
|
||||
* @returns {boolean} Returns true if the ImageHiPS represents a planetary body; otherwise, returns false.
|
||||
*/
|
||||
ImageHiPS.prototype.isPlanetaryBody = function () {
|
||||
return this.hipsBody !== undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the image format for the ImageHiPS.
|
||||
*
|
||||
* This method updates the image format of the ImageHiPS, performs format validation, and triggers the update of metadata.
|
||||
*
|
||||
* @memberof ImageHiPS
|
||||
*
|
||||
* @param {string} format - The desired image format. Should be one of ["fits", "png", "jpg", "webp"].
|
||||
*
|
||||
* @throws {string} Throws an error if the provided format is not one of the supported formats or if the format is not available for the specific ImageHiPS.
|
||||
*/
|
||||
ImageHiPS.prototype.setImageFormat = function (format) {
|
||||
let self = this;
|
||||
self.query.then(() => {
|
||||
self._updateMetadata(() => {
|
||||
let imgFormat = format.toLowerCase();
|
||||
|
||||
if (
|
||||
imgFormat !== "fits" &&
|
||||
imgFormat !== "png" &&
|
||||
imgFormat !== "jpg" &&
|
||||
imgFormat !== "jpeg" &&
|
||||
imgFormat !== "webp"
|
||||
) {
|
||||
throw 'Formats must lie in ["fits", "png", "jpg", "webp"]';
|
||||
}
|
||||
|
||||
if (imgFormat === "jpg") {
|
||||
imgFormat = "jpeg";
|
||||
}
|
||||
|
||||
// Passed the check, we erase the image format with the new one
|
||||
// We do nothing if the imgFormat is the same
|
||||
if (self.imgFormat === imgFormat) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the properties to see if the given format is available among the list
|
||||
// If the properties have not been retrieved yet, it will be tested afterwards
|
||||
const availableFormats = self.formats;
|
||||
// user wants a fits but the metadata tells this format is not available
|
||||
if (
|
||||
imgFormat === "fits" &&
|
||||
availableFormats.indexOf("fits") < 0
|
||||
) {
|
||||
throw self.id + " does not provide fits tiles";
|
||||
}
|
||||
|
||||
if (
|
||||
imgFormat === "webp" &&
|
||||
availableFormats.indexOf("webp") < 0
|
||||
) {
|
||||
throw self.id + " does not provide webp tiles";
|
||||
}
|
||||
|
||||
if (
|
||||
imgFormat === "png" &&
|
||||
availableFormats.indexOf("png") < 0
|
||||
) {
|
||||
throw self.id + " does not provide png tiles";
|
||||
}
|
||||
|
||||
if (
|
||||
imgFormat === "jpeg" &&
|
||||
availableFormats.indexOf("jpeg") < 0
|
||||
) {
|
||||
throw self.id + " does not provide jpeg tiles";
|
||||
}
|
||||
|
||||
// Switch from png/webp/jpeg to fits
|
||||
if (
|
||||
(self.imgFormat === "png" ||
|
||||
self.imgFormat === "webp" ||
|
||||
self.imgFormat === "jpeg") &&
|
||||
imgFormat === "fits"
|
||||
) {
|
||||
if (self.minCut && self.maxCut) {
|
||||
// reset cuts to those given from the properties
|
||||
self.setCuts(self.minCut, self.maxCut);
|
||||
}
|
||||
// Switch from fits to png/webp/jpeg
|
||||
} else if (self.imgFormat === "fits") {
|
||||
self.setCuts(0.0, 1.0);
|
||||
}
|
||||
|
||||
// Check if it is a fits
|
||||
self.imgFormat = imgFormat;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the opacity factor when rendering the ImageHiPS
|
||||
*
|
||||
* @memberof ImageHiPS
|
||||
*
|
||||
* @returns {string[]} Returns the formats accepted for the survey, i.e. the formats of tiles that are availables. Could be PNG, WEBP, JPG and FITS.
|
||||
*/
|
||||
ImageHiPS.prototype.getAvailableFormats = function () {
|
||||
return this.formats;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the opacity factor when rendering the ImageHiPS
|
||||
*
|
||||
* @memberof ImageHiPS
|
||||
*
|
||||
* @param {number} opacity - Opacity of the survey to set. Between 0 and 1
|
||||
*/
|
||||
ImageHiPS.prototype.setOpacity = function (opacity) {
|
||||
this._updateMetadata(() => {
|
||||
this.colorCfg.setOpacity(opacity);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the blending mode when rendering the ImageHiPS
|
||||
*
|
||||
* @memberof ImageHiPS
|
||||
*
|
||||
* @param {boolean} [additive=false] -
|
||||
*
|
||||
* @description Two rendering modes are availables i.e. the default one and the additive one.
|
||||
* When rendering this survey on top of the already rendered ones, the final color of the screen is computed like:
|
||||
* <br>
|
||||
* <br>opacity * this_survey_color + (1 - opacity) * already_rendered_color for the default mode
|
||||
* <br>opacity * this_survey_color + already_rendered_color for the additive mode
|
||||
* <br>
|
||||
* <br>
|
||||
* Additive mode allows you to do linear survey color combination i.e. let's define 3 surveys named s1, s2, s3. Each could be associated to one color channel, i.e. s1 with red, s2 with green and s3 with the blue color channel.
|
||||
* If the additive blending mode is enabled, then the final pixel color of your screen will be: rgb = [s1_opacity * s1_color; s2_opacity * s2_color; s3_opacity * s3_color]
|
||||
*/
|
||||
ImageHiPS.prototype.setBlendingConfig = function (additive = false) {
|
||||
this._updateMetadata(() => {
|
||||
this.colorCfg.setBlendingConfig(additive);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the colormap when rendering the ImageHiPS.
|
||||
*
|
||||
* @memberof ImageHiPS
|
||||
*
|
||||
* @param {string} [colormap="grayscale"] - The colormap label to use. See {@link https://matplotlib.org/stable/users/explain/colors/colormaps.html|here} for more info about colormaps.
|
||||
* Possible values are:
|
||||
* <br>"blues"
|
||||
* <br>"cividis"
|
||||
* <br>"cubehelix"
|
||||
* <br>"eosb"
|
||||
* <br>"grayscale"
|
||||
* <br>"inferno"
|
||||
* <br>"magma"
|
||||
* <br>"native"
|
||||
* <br>"parula"
|
||||
* <br>"plasma"
|
||||
* <br>"rainbow"
|
||||
* <br>"rdbu"
|
||||
* <br>"rdylbu"
|
||||
* <br>"redtemperature"
|
||||
* <br>"sinebow"
|
||||
* <br>"spectral"
|
||||
* <br>"summer"
|
||||
* <br>"viridis"
|
||||
* <br>"ylgnbu"
|
||||
* <br>"ylorbr"
|
||||
* <br>"red"
|
||||
* <br>"green"
|
||||
* <br>"blue"
|
||||
* @param {Object} [options] - Options for the colormap
|
||||
* @param {string} [options.stretch] - Stretching function of the colormap. Possible values are 'linear', 'asinh', 'log', 'sqrt', 'pow'. If no given, will not change it.
|
||||
* @param {boolean} [options.reversed=false] - Reverse the colormap axis.
|
||||
*/
|
||||
ImageHiPS.prototype.setColormap = function (colormap, options) {
|
||||
this._updateMetadata(() => {
|
||||
this.colorCfg.setColormap(colormap, options);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the gamma correction factor for the ImageHiPS.
|
||||
*
|
||||
* This method updates the gamma of the ImageHiPS.
|
||||
*
|
||||
* @memberof ImageHiPS
|
||||
*
|
||||
* @param {number} lowCut - The low cut value to set for the ImageHiPS.
|
||||
* @param {number} highCut - The high cut value to set for the ImageHiPS.
|
||||
*/
|
||||
ImageHiPS.prototype.setCuts = function (lowCut, highCut) {
|
||||
this._updateMetadata(() => {
|
||||
this.colorCfg.setCuts(lowCut, highCut);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the gamma correction factor for the ImageHiPS.
|
||||
*
|
||||
* This method updates the gamma of the ImageHiPS.
|
||||
*
|
||||
* @memberof ImageHiPS
|
||||
*
|
||||
* @param {number} gamma - The saturation value to set for the ImageHiPS. Between 0.1 and 10
|
||||
*/
|
||||
ImageHiPS.prototype.setGamma = function (gamma) {
|
||||
this._updateMetadata(() => {
|
||||
this.colorCfg.setGamma(gamma);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the saturation for the ImageHiPS.
|
||||
*
|
||||
* This method updates the saturation of the ImageHiPS.
|
||||
*
|
||||
* @memberof ImageHiPS
|
||||
*
|
||||
* @param {number} saturation - The saturation value to set for the ImageHiPS. Between 0 and 1
|
||||
*/
|
||||
ImageHiPS.prototype.setSaturation = function (saturation) {
|
||||
this._updateMetadata(() => {
|
||||
this.colorCfg.setSaturation(saturation);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the brightness for the ImageHiPS.
|
||||
*
|
||||
* This method updates the brightness of the ImageHiPS.
|
||||
*
|
||||
* @memberof ImageHiPS
|
||||
*
|
||||
* @param {number} brightness - The brightness value to set for the ImageHiPS. Between 0 and 1
|
||||
*/
|
||||
ImageHiPS.prototype.setBrightness = function (brightness) {
|
||||
this._updateMetadata(() => {
|
||||
this.colorCfg.setBrightness(brightness);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the contrast for the ImageHiPS.
|
||||
*
|
||||
* This method updates the contrast of the ImageHiPS and triggers the update of metadata.
|
||||
*
|
||||
* @memberof ImageHiPS
|
||||
*
|
||||
* @param {number} contrast - The contrast value to set for the ImageHiPS. Between 0 and 1
|
||||
*/
|
||||
ImageHiPS.prototype.setContrast = function (contrast) {
|
||||
this._updateMetadata(() => {
|
||||
this.colorCfg.setContrast(contrast);
|
||||
});
|
||||
};
|
||||
|
||||
// Private method for updating the backend with the new meta
|
||||
ImageHiPS.prototype._updateMetadata = function (callback) {
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
|
||||
// Tell the view its meta have changed
|
||||
try {
|
||||
if (this.added) {
|
||||
this.view.wasm.setImageMetadata(this.layer, {
|
||||
...this.colorCfg.get(),
|
||||
longitudeReversed: this.longitudeReversed,
|
||||
imgFormat: this.imgFormat,
|
||||
});
|
||||
// once the meta have been well parsed, we can set the meta
|
||||
ALEvent.HIPS_LAYER_CHANGED.dispatchedTo(this.view.aladinDiv, {
|
||||
layer: this,
|
||||
});
|
||||
}
|
||||
|
||||
// save it in the JS HiPS cache
|
||||
this._saveInCache();
|
||||
} catch (e) {
|
||||
// Display the error message
|
||||
console.error(e);
|
||||
}
|
||||
};
|
||||
|
||||
ImageHiPS.prototype.add = function (layer) {
|
||||
this.layer = layer;
|
||||
let self = this;
|
||||
|
||||
this.view.wasm.addImageHiPS({
|
||||
layer,
|
||||
properties: {
|
||||
creatorDid: self.creatorDid,
|
||||
url: self.url,
|
||||
maxOrder: self.maxOrder,
|
||||
cooFrame: self.cooFrame,
|
||||
tileSize: self.tileSize,
|
||||
formats: self.formats,
|
||||
bitpix: self.numBitsPerPixel,
|
||||
skyFraction: self.skyFraction,
|
||||
minOrder: self.minOrder,
|
||||
hipsInitialFov: self.initialFov,
|
||||
hipsInitialRa: self.initialRa,
|
||||
hipsInitialDec: self.initialDec,
|
||||
isPlanetaryBody: self.isPlanetaryBody(),
|
||||
hipsBody: self.hipsBody,
|
||||
},
|
||||
meta: {
|
||||
...this.colorCfg.get(),
|
||||
longitudeReversed: this.longitudeReversed,
|
||||
imgFormat: this.imgFormat,
|
||||
},
|
||||
});
|
||||
|
||||
return Promise.resolve(this)
|
||||
.then((hips) => {
|
||||
if (hips.successCallback) {
|
||||
hips.successCallback(hips)
|
||||
}
|
||||
|
||||
return hips
|
||||
});
|
||||
};
|
||||
|
||||
// @api
|
||||
ImageHiPS.prototype.toggle = function () {
|
||||
if (this.colorCfg.getOpacity() != 0.0) {
|
||||
this.colorCfg.setOpacity(0.0);
|
||||
} else {
|
||||
this.colorCfg.setOpacity(this.prevOpacity);
|
||||
}
|
||||
};
|
||||
|
||||
// @oldapi
|
||||
ImageHiPS.prototype.setAlpha = ImageHiPS.prototype.setOpacity;
|
||||
|
||||
ImageHiPS.prototype.setColorCfg = function (colorCfg) {
|
||||
this._updateMetadata(() => {
|
||||
this.colorCfg = colorCfg;
|
||||
});
|
||||
};
|
||||
|
||||
// @api
|
||||
ImageHiPS.prototype.getColorCfg = function () {
|
||||
return this.colorCfg;
|
||||
};
|
||||
|
||||
// @api
|
||||
ImageHiPS.prototype.getOpacity = function () {
|
||||
return this.colorCfg.getOpacity();
|
||||
};
|
||||
|
||||
ImageHiPS.prototype.getAlpha = ImageHiPS.prototype.getOpacity;
|
||||
|
||||
// @api
|
||||
ImageHiPS.prototype.readPixel = function (x, y) {
|
||||
return this.view.wasm.readPixel(x, y, this.layer);
|
||||
};
|
||||
|
||||
ImageHiPS.DEFAULT_SURVEY_ID = "CDS/P/DSS2/color";
|
||||
|
||||
return ImageHiPS;
|
||||
})();
|
||||
1009
src/js/ImageSurvey.js
Normal file
1009
src/js/ImageSurvey.js
Normal file
File diff suppressed because it is too large
Load Diff
212
src/js/Line.js
212
src/js/Line.js
@@ -29,161 +29,77 @@
|
||||
* Author: Matthieu Baumann[CDS]
|
||||
*
|
||||
*****************************************************************************/
|
||||
import { Polyline } from "./Polyline.js";
|
||||
import { Utils } from './Utils';
|
||||
import { Overlay } from "./Overlay.js";
|
||||
import { Ellipse } from "./Ellipse.js";
|
||||
|
||||
/**
|
||||
* Represents an line shape
|
||||
*
|
||||
* @namespace
|
||||
* @typedef {Object} Line
|
||||
*/
|
||||
export let Line = (function() {
|
||||
/**
|
||||
* Constructor function for creating a new line.
|
||||
*
|
||||
* @constructor
|
||||
* @memberof Line
|
||||
* @param {number} ra1 - Right Ascension (RA) coordinate of the center in degrees.
|
||||
* @param {number} dec1 - Declination (Dec) coordinate of the center in degrees.
|
||||
* @param {number} ra2 - Right Ascension (RA) coordinate of the center in degrees.
|
||||
* @param {number} dec2 - Declination (Dec) coordinate of the center in degrees.
|
||||
* @param {CooFrame} [frame] - Frame in which the coordinates are given. If none, the frame used is icrs/j2000.
|
||||
* @param {ShapeOptions} options - Options for configuring the line. Additional properties:
|
||||
* @param {boolean} [options.arrow=false] - Add an arrow pointing from (ra1, dec1) to (ra2, dec2)
|
||||
*
|
||||
* @returns {Line} - The line shape object
|
||||
*/
|
||||
let Line = function(ra1, dec1, ra2, dec2, frame, options) {
|
||||
options = options || {};
|
||||
this.color = options['color'] || undefined;
|
||||
this.opacity = options['opacity'] || undefined;
|
||||
this.lineWidth = options["lineWidth"] || undefined;
|
||||
this.selectionColor = options["selectionColor"] || '#00ff00';
|
||||
this.hoverColor = options["hoverColor"] || undefined;
|
||||
this.arrow = options["arrow"] === undefined ? false : options["arrow"];
|
||||
|
||||
// All graphics overlay have an id
|
||||
this.id = 'line-' + Utils.uuidv4();
|
||||
|
||||
this.overlay = null;
|
||||
|
||||
this.isShowing = true;
|
||||
this.isSelected = false;
|
||||
this.isHovered = false;
|
||||
|
||||
this.ra1 = ra1;
|
||||
this.dec1 = dec1;
|
||||
this.ra2 = ra2;
|
||||
this.dec2 = dec2;
|
||||
this.frame = frame;
|
||||
// constructor
|
||||
let Line = function(x1, y1, x2, y2) {
|
||||
this.x1 = x1;
|
||||
this.y1 = y1;
|
||||
this.x2 = x2;
|
||||
this.y2 = y2;
|
||||
};
|
||||
|
||||
Line.prototype = {
|
||||
setOverlay: Polyline.prototype.setOverlay,
|
||||
isFootprint: Polyline.prototype.isFootprint,
|
||||
show: Polyline.prototype.show,
|
||||
hide: Polyline.prototype.hide,
|
||||
|
||||
select: Polyline.prototype.select,
|
||||
deselect: Polyline.prototype.deselect,
|
||||
|
||||
hover: Polyline.prototype.hover,
|
||||
unhover: Polyline.prototype.unhover,
|
||||
|
||||
getLineWidth: Polyline.prototype.getLineWidth,
|
||||
setLineWidth: Polyline.prototype.setLineWidth,
|
||||
|
||||
setColor: Polyline.prototype.setColor,
|
||||
setSelectionColor: Polyline.prototype.setSelectionColor,
|
||||
setHoverColor: Polyline.prototype.setHoverColor,
|
||||
|
||||
draw: function(ctx, view, noStroke, noSmallCheck) {
|
||||
noStroke = noStroke===true || false;
|
||||
noSmallCheck = noSmallCheck===true || false;
|
||||
// project
|
||||
const v1 = view.aladin.world2pix(this.ra1, this.dec1, this.frame);
|
||||
if (!v1)
|
||||
return false;
|
||||
const v2 = view.aladin.world2pix(this.ra2, this.dec2, this.frame);
|
||||
if (!v2)
|
||||
return false;
|
||||
|
||||
const xmin = Math.min(v1[0], v2[0]);
|
||||
const xmax = Math.max(v1[0], v2[0]);
|
||||
const ymin = Math.min(v1[1], v2[1]);
|
||||
const ymax = Math.max(v1[1], v2[1]);
|
||||
|
||||
// out of bbox
|
||||
if (xmax < 0 || xmin > view.width || ymax < 0 || ymin > view.height) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let baseColor = this.color || (this.overlay && this.overlay.color) || '#ff0000';
|
||||
let lineWidth = this.lineWidth || this.overlay.lineWidth || 3;
|
||||
|
||||
// too small
|
||||
if(!noSmallCheck) {
|
||||
this.isTooSmall = (xmax - xmin) < 1 && (ymax - ymin) < 1;
|
||||
if (this.isTooSmall) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.isSelected) {
|
||||
ctx.strokeStyle = this.selectionColor || Overlay.increaseBrightness(baseColor, 50);
|
||||
} else if (this.isHovered) {
|
||||
ctx.strokeStyle = this.hoverColor || Overlay.increaseBrightness(baseColor, 25);
|
||||
} else {
|
||||
ctx.strokeStyle = baseColor;
|
||||
}
|
||||
|
||||
ctx.lineWidth = lineWidth;
|
||||
ctx.globalAlpha = this.opacity;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(v1[0], v1[1]);
|
||||
ctx.lineTo(v2[0], v2[1]);
|
||||
|
||||
if (this.arrow) {
|
||||
// draw the arrow
|
||||
var angle, x, y, xh, yh;
|
||||
var arrowRad = this.lineWidth * 3;
|
||||
|
||||
angle = Math.atan2(v2[1] - v1[1], v2[0] - v1[0])
|
||||
xh = v2[0];
|
||||
yh = v2[1];
|
||||
|
||||
//ctx.moveTo(xh, yh);
|
||||
|
||||
var t = angle + Math.PI * 3 / 4;
|
||||
x = arrowRad * Math.cos(t) + v2[0];
|
||||
y = arrowRad * Math.sin(t) + v2[1];
|
||||
|
||||
ctx.moveTo(x, y);
|
||||
ctx.lineTo(xh, yh);
|
||||
|
||||
var t = angle - Math.PI * 3 / 4;
|
||||
x = arrowRad *Math.cos(t) + v2[0];
|
||||
y = arrowRad *Math.sin(t) + v2[1];
|
||||
|
||||
ctx.lineTo(x, y);
|
||||
}
|
||||
|
||||
if (!noStroke) {
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
// Method for testing whether a line is inside the view
|
||||
// http://www.jeffreythompson.org/collision-detection/line-rect.php
|
||||
Line.prototype.isInsideView = function(rw, rh) {
|
||||
if (this.x1 >= 0 && this.x1 <= rw && this.y1 >= 0 && this.y1 <= rh) {
|
||||
return true;
|
||||
},
|
||||
}
|
||||
if (this.x2 >= 0 && this.x2 <= rw && this.y2 >= 0 && this.y2 <= rh) {
|
||||
return true;
|
||||
}
|
||||
|
||||
isInStroke: Ellipse.prototype.isInStroke,
|
||||
// check if the line has hit any of the rectangle's sides
|
||||
// uses the Line/Line function below
|
||||
let left = Line.intersectLine(this.x1, this.y1, this.x2, this.y2, 0, 0, 0, rh);
|
||||
let right = Line.intersectLine(this.x1, this.y1, this.x2, this.y2, rw, 0, rw, rh);
|
||||
let top = Line.intersectLine(this.x1, this.y1, this.x2, this.y2, 0, 0, rw, 0);
|
||||
let bottom = Line.intersectLine(this.x1, this.y1, this.x2, this.y2, 0, rh, rw, rh);
|
||||
|
||||
// if ANY of the above are true, the line
|
||||
// has hit the rectangle
|
||||
if (left || right || top || bottom) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/*Line.prototype.intersectsBBox = function(x, y, w, h) {
|
||||
// todo
|
||||
};*/
|
||||
return false;
|
||||
};
|
||||
|
||||
Line.prototype.isFootprint = function() {
|
||||
return false;
|
||||
}
|
||||
|
||||
Line.prototype.draw = function(ctx, noStroke) {
|
||||
noStroke = noStroke===true || false;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(this.x1, this.y1);
|
||||
ctx.lineTo(this.x2, this.y2);
|
||||
|
||||
if (!noStroke) {
|
||||
ctx.stroke();
|
||||
}
|
||||
};
|
||||
|
||||
Line.intersectLine = function(x1, y1, x2, y2, x3, y3, x4, y4) {
|
||||
// Calculate the direction of the lines
|
||||
let uA = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
|
||||
let uB = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
|
||||
|
||||
// If uA and uB are between 0-1, lines are colliding
|
||||
if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
Line.prototype.isInStroke = function(ctx, view, x, y) {
|
||||
this.draw(ctx, view, true);
|
||||
return ctx.isPointInStroke(x, y);
|
||||
};
|
||||
|
||||
Line.prototype.intersectsBBox = function(x, y, w, h) {
|
||||
// todo
|
||||
};
|
||||
|
||||
return Line;
|
||||
|
||||
@@ -15,35 +15,7 @@ import { Color } from "./Color.js";
|
||||
|
||||
import { ALEvent } from "./events/ALEvent.js";
|
||||
|
||||
/**
|
||||
* @typedef {Object} MOCOptions
|
||||
* @description Options for configuring a MOC (Multi-Order-Coverage).
|
||||
*
|
||||
* @property {Object} options - Configuration options for the MOC.
|
||||
* @property {string} [options.name="MOC"] - The name of the catalog.
|
||||
* @property {string} [options.color] - The color of the MOC HEALPix cell edges.
|
||||
* @property {string} [options.fillColor] - A filling color of the MOC HEALPix cells.
|
||||
* @property {string} [options.fill=false] - Fill the MOC with `options.fillColor`
|
||||
* @property {string} [options.edge=true] - Draw the edges of the HEALPix cells with `options.color`.
|
||||
* @property {number} [options.lineWidth=3] - The line width in pixels
|
||||
* @property {Boolean} [options.perimeter=false] - A filling color of the MOC HEALPix cells.
|
||||
* @property {number} [options.opacity=1.0] - The opacity of the MOC
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a Multi-Order-Coverage with configurable options for display and interaction.
|
||||
*
|
||||
* @namespace
|
||||
* @typedef {Object} MOC
|
||||
*/
|
||||
export let MOC = (function() {
|
||||
/**
|
||||
* Constructor function for creating a new catalog instance.
|
||||
*
|
||||
* @constructor
|
||||
* @memberof MOC
|
||||
* @param {MOCOptions} options - Configuration options for the MOC.
|
||||
*/
|
||||
let MOC = function(options) {
|
||||
//this.order = undefined;
|
||||
|
||||
@@ -89,7 +61,7 @@ export let MOC = (function() {
|
||||
}
|
||||
|
||||
this.opacity = Math.max(0, Math.min(1, this.opacity)); // 0 <= this.opacity <= 1
|
||||
this.lineWidth = options["lineWidth"] || 3;
|
||||
this.lineWidth = options["lineWidth"] || 1;
|
||||
|
||||
//this.proxyCalled = false; // this is a flag to check whether we already tried to load the MOC through the proxy
|
||||
|
||||
@@ -110,7 +82,7 @@ export let MOC = (function() {
|
||||
* set MOC data by parsing a MOC serialized in JSON
|
||||
* (as defined in IVOA MOC document, section 3.1.1)
|
||||
*/
|
||||
MOC.prototype.parse = function(data, successCallback, errorCallback) {
|
||||
MOC.prototype.parse = function(data, successCallback) {
|
||||
if (typeof data === 'string' || data instanceof String) {
|
||||
let url = data;
|
||||
this.promiseFetchData = fetch(url)
|
||||
@@ -120,7 +92,7 @@ export let MOC = (function() {
|
||||
}
|
||||
|
||||
this.successCallback = successCallback;
|
||||
this.errorCallback = errorCallback;
|
||||
this.errorCallback = this.errorCallback;
|
||||
};
|
||||
|
||||
MOC.prototype.setView = function(view) {
|
||||
@@ -166,11 +138,7 @@ export let MOC = (function() {
|
||||
|
||||
self.view.requestRedraw();
|
||||
})
|
||||
.catch(e => {
|
||||
console.error('MOC load error:' + e)
|
||||
if (self.errorCallback)
|
||||
self.errorCallback(self);
|
||||
})
|
||||
.catch(e => alert('MOC load error:' + e))
|
||||
};
|
||||
|
||||
MOC.prototype.reportChange = function() {
|
||||
|
||||
@@ -51,62 +51,19 @@ export class MocServer {
|
||||
expr: "dataproduct_type=image",
|
||||
get: "record",
|
||||
fmt: "json",
|
||||
fields: "ID,hips_creator,hips_copyright,hips_order,hips_tile_width,hips_frame,hips_tile_format,obs_title,obs_description,obs_copyright,obs_regime",
|
||||
fields: "ID,hips_creator,hips_copyright,hips_frame,hips_tile_format,obs_title,obs_description,obs_copyright,obs_regime",
|
||||
//fields: "ID,hips_initial_fov,hips_initial_ra,hips_initial_dec,hips_pixel_bitpix,hips_creator,hips_copyright,hips_frame,hips_order,hips_order_min,hips_tile_width,hips_tile_format,hips_pixel_cut,obs_title,obs_description,obs_copyright,obs_regime,hips_data_range,hips_service_url",
|
||||
};
|
||||
|
||||
this._allHiPSes = Utils.loadFromUrls(MocServer.MIRRORS_HTTPS, {
|
||||
data: params,
|
||||
dataType: 'json',
|
||||
desc: 'MOCServer query to get all the HiPS metadata'
|
||||
dataType: 'json'
|
||||
})
|
||||
}
|
||||
|
||||
return this._allHiPSes;
|
||||
}
|
||||
|
||||
static getAllHiPSesInsideView(aladin) {
|
||||
let params = {
|
||||
//expr: "dataproduct_type=image||dataproduct_type=cube",
|
||||
expr: "dataproduct_type=image",
|
||||
get: "record",
|
||||
fmt: "json",
|
||||
fields: "ID",
|
||||
};
|
||||
|
||||
try {
|
||||
const corners = aladin.getFoVCorners(1, 'icrs');
|
||||
let stc = 'Polygon '
|
||||
for (var radec of corners) {
|
||||
stc += radec[0] + ' ' + radec[1] + ' ';
|
||||
}
|
||||
|
||||
params['stc'] = stc;
|
||||
} catch (e) {}
|
||||
|
||||
return Utils.loadFromUrls(MocServer.MIRRORS_HTTPS, {
|
||||
data: params,
|
||||
dataType: 'json',
|
||||
desc: 'MOCServer: Retrieve HiPS inside FoV'
|
||||
})
|
||||
}
|
||||
|
||||
static getHiPSesFromIDs(ids) {
|
||||
const params = {
|
||||
//expr: "dataproduct_type=image||dataproduct_type=cube",
|
||||
expr: "dataproduct_type=image&&ID=" + ids.join(','),
|
||||
get: "record",
|
||||
fmt: "json",
|
||||
fields: "ID,hips_creator,hips_copyright,hips_frame,hips_tile_format,obs_title,obs_description,obs_copyright,obs_regime",
|
||||
//fields: "ID,hips_initial_fov,hips_initial_ra,hips_initial_dec,hips_pixel_bitpix,hips_creator,hips_copyright,hips_frame,hips_order,hips_order_min,hips_tile_width,hips_tile_format,hips_pixel_cut,obs_title,obs_description,obs_copyright,obs_regime,hips_data_range,hips_service_url",
|
||||
};
|
||||
|
||||
return Utils.loadFromUrls(MocServer.MIRRORS_HTTPS, {
|
||||
data: params,
|
||||
dataType: 'json'
|
||||
})
|
||||
}
|
||||
|
||||
static getAllCatalogHiPSes() {
|
||||
if (!this._allCatalogHiPSes) {
|
||||
const params = {
|
||||
|
||||
@@ -33,41 +33,24 @@
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
import { AladinUtils } from './AladinUtils.js';
|
||||
import { Line } from './Line.js';
|
||||
import { Utils } from './Utils';
|
||||
import { Overlay } from "./Overlay.js";
|
||||
import { ProjectionEnum } from "./ProjectionEnum.js";
|
||||
|
||||
|
||||
/**
|
||||
* Represents a polyline shape
|
||||
*
|
||||
* @namespace
|
||||
* @typedef {Object} Polyline
|
||||
*/
|
||||
export let Polyline = (function() {
|
||||
|
||||
function _calculateMag2ForNoSinProjections(l, view) {
|
||||
export let Polyline= (function() {
|
||||
function _calculateMag2ForNoSinProjections(line, view) {
|
||||
// check if the line is too big (in the clip space) to be drawn
|
||||
const [x1, y1] = view.wasm.screenToClip(l.x1, l.y1);
|
||||
const [x2, y2] = view.wasm.screenToClip(l.x2, l.y2);
|
||||
const [x1, y1] = view.wasm.screenToClip(line.x1, line.y1);
|
||||
const [x2, y2] = view.wasm.screenToClip(line.x2, line.y2);
|
||||
|
||||
const mag2 = (x1 - x2)*(x1 - x2) + (y1 - y2)*(y1 - y2);
|
||||
return mag2;
|
||||
}
|
||||
|
||||
function _drawLine(l, ctx, noStroke) {
|
||||
noStroke = noStroke===true || false;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(l.x1, l.y1);
|
||||
ctx.lineTo(l.x2, l.y2);
|
||||
|
||||
if (!noStroke) {
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
/*function _isAcrossCollignonZoneForHpxProjection(line, view) {
|
||||
function _isAcrossCollignonZoneForHpxProjection(line, view) {
|
||||
const [x1, y1] = view.wasm.screenToClip(line.x1, line.y1);
|
||||
const [x2, y2] = view.wasm.screenToClip(line.x2, line.y2);
|
||||
|
||||
@@ -90,19 +73,9 @@ export let Polyline = (function() {
|
||||
}
|
||||
|
||||
return false;
|
||||
}*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor function for creating a new polyline.
|
||||
*
|
||||
* @constructor
|
||||
* @memberof Polyline
|
||||
* @param {Array.<number[]>} radecArray - right-ascension/declination 2-tuple array describing the polyline's vertices in degrees
|
||||
* @param {ShapeOptions} options - Configuration options for the polyline. Additional properties:
|
||||
* @param {boolean} [options.closed=false] - Close the polyline, default to false.
|
||||
*
|
||||
* @returns {Polyline} - The polyline shape object
|
||||
*/
|
||||
// constructor
|
||||
let Polyline = function(radecArray, options) {
|
||||
options = options || {};
|
||||
this.color = options['color'] || undefined;
|
||||
@@ -113,7 +86,11 @@ export let Polyline = (function() {
|
||||
this.selectionColor = options["selectionColor"] || '#00ff00';
|
||||
this.hoverColor = options["hoverColor"] || undefined;
|
||||
|
||||
this.closed = (options["closed"] !== undefined) ? options["closed"] : false;
|
||||
if (options["closed"]) {
|
||||
this.closed = options["closed"];
|
||||
} else {
|
||||
this.closed = false;
|
||||
}
|
||||
|
||||
// All graphics overlay have an id
|
||||
this.id = 'polyline-' + Utils.uuidv4();
|
||||
@@ -206,10 +183,9 @@ export let Polyline = (function() {
|
||||
};
|
||||
|
||||
Polyline.prototype.setColor = function(color) {
|
||||
if (!color || this.color == color) {
|
||||
if (this.color == color) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.color = color;
|
||||
if (this.overlay) {
|
||||
this.overlay.reportChange();
|
||||
@@ -217,7 +193,7 @@ export let Polyline = (function() {
|
||||
};
|
||||
|
||||
Polyline.prototype.setSelectionColor = function(color) {
|
||||
if (!color || this.selectionColor == color) {
|
||||
if (this.selectionColor == color) {
|
||||
return;
|
||||
}
|
||||
this.selectionColor = color;
|
||||
@@ -227,7 +203,7 @@ export let Polyline = (function() {
|
||||
};
|
||||
|
||||
Polyline.prototype.setHoverColor = function(color) {
|
||||
if (!color || this.hoverColor == color) {
|
||||
if (this.hoverColor == color) {
|
||||
return;
|
||||
}
|
||||
this.hoverColor = color;
|
||||
@@ -243,11 +219,11 @@ export let Polyline = (function() {
|
||||
|
||||
Polyline.prototype.draw = function(ctx, view, noStroke) {
|
||||
if (! this.isShowing) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (! this.radecArray || this.radecArray.length<2) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
noStroke = noStroke===true || false;
|
||||
@@ -261,7 +237,7 @@ export let Polyline = (function() {
|
||||
}
|
||||
|
||||
if (!this.lineWidth) {
|
||||
this.lineWidth = (this.overlay && this.overlay.lineWidth) || 2;
|
||||
this.lineWidth = this.overlay.lineWidth || 2;
|
||||
}
|
||||
|
||||
if (this.isSelected) {
|
||||
@@ -287,9 +263,9 @@ export let Polyline = (function() {
|
||||
let ymax = Number.NEGATIVE_INFINITY;
|
||||
|
||||
for (var k=0; k<len; k++) {
|
||||
var xyview = view.aladin.world2pix(this.radecArray[k][0], this.radecArray[k][1]);
|
||||
var xyview = AladinUtils.radecToViewXy(this.radecArray[k][0], this.radecArray[k][1], view.aladin);
|
||||
if (!xyview) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
xyView.push({x: xyview[0], y: xyview[1]});
|
||||
@@ -300,13 +276,9 @@ export let Polyline = (function() {
|
||||
ymax = Math.max(ymax, xyview[1]);
|
||||
}
|
||||
|
||||
// 2. do not draw the polygon if it lies in less than linewidth pixels
|
||||
if (xmax < 0 || xmin > view.width || ymax < 0 || ymin > view.height) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((xmax - xmin) < this.lineWidth || (ymax - ymin) < this.lineWidth) {
|
||||
return false;
|
||||
// 2. do not draw the polygon if it lies in less than 1 pixel
|
||||
if ((xmax - xmin) < 1 || (ymax - ymin) < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
let drawLine;
|
||||
@@ -314,28 +286,27 @@ export let Polyline = (function() {
|
||||
|
||||
if (view.projection === ProjectionEnum.SIN) {
|
||||
drawLine = (v0, v1) => {
|
||||
const l = {x1: v0.x, y1: v0.y, x2: v1.x, y2: v1.y};
|
||||
const line = new Line(v0.x, v0.y, v1.x, v1.y);
|
||||
|
||||
if (Polyline.isInsideView(l.x1, l.y1, l.x2, l.y2, view.width, view.height)) {
|
||||
_drawLine(l, ctx);
|
||||
if (line.isInsideView(view.width, view.height)) {
|
||||
line.draw(ctx);
|
||||
}
|
||||
};
|
||||
|
||||
if (this.closed && this.fill) {
|
||||
fillPoly = (v0, v1, index) => {
|
||||
const l = {x1: v0.x, y1: v0.y, x2: v1.x, y2: v1.y};
|
||||
|
||||
const line = new Line(v0.x, v0.y, v1.x, v1.y);
|
||||
if (index === 0) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(l.x1, l.y1);
|
||||
ctx.moveTo(line.x1, line.y1);
|
||||
} else {
|
||||
ctx.lineTo(l.x1, l.y1);
|
||||
ctx.lineTo(line.x1, line.y1);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
/*} else if (view.projection === ProjectionEnum.HPX) {
|
||||
} else if (view.projection === ProjectionEnum.HPX) {
|
||||
drawLine = (v0, v1) => {
|
||||
const line = new Line(v0.x, v0.y, v1.x, v1.y);
|
||||
|
||||
@@ -375,31 +346,31 @@ export let Polyline = (function() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}*/
|
||||
}
|
||||
} else {
|
||||
drawLine = (v0, v1) => {
|
||||
const l = {x1: v0.x, y1: v0.y, x2: v1.x, y2: v1.y};
|
||||
const line = new Line(v0.x, v0.y, v1.x, v1.y);
|
||||
|
||||
if (Polyline.isInsideView(l.x1, l.y1, l.x2, l.y2, view.width, view.height)) {
|
||||
const mag2 = _calculateMag2ForNoSinProjections(l, view);
|
||||
if (line.isInsideView(view.width, view.height)) {
|
||||
const mag2 = _calculateMag2ForNoSinProjections(line, view);
|
||||
|
||||
if (mag2 < 0.1) {
|
||||
_drawLine(l, ctx);
|
||||
line.draw(ctx);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (this.closed && this.fill) {
|
||||
fillPoly = (v0, v1, index) => {
|
||||
const l = {x1: v0.x, y1: v0.y, x2: v1.x, y2: v1.y};
|
||||
const line = new Line(v0.x, v0.y, v1.x, v1.y);
|
||||
|
||||
const mag2 = _calculateMag2ForNoSinProjections(l, view);
|
||||
const mag2 = _calculateMag2ForNoSinProjections(line, view);
|
||||
|
||||
if (mag2 < 0.1) {
|
||||
if (index === 0) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(l.x1, l.y1);
|
||||
ctx.moveTo(line.x1, line.y1);
|
||||
} else {
|
||||
ctx.lineTo(l.x1, l.y1);
|
||||
ctx.lineTo(line.x1, line.y1);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -444,15 +415,13 @@ export let Polyline = (function() {
|
||||
v1 = v1 + 1;
|
||||
}
|
||||
|
||||
//ctx.globalAlpha = 1;
|
||||
ctx.globalAlpha = 1;
|
||||
ctx.save();
|
||||
ctx.fillStyle = this.fillColor;
|
||||
ctx.globalAlpha = this.opacity;
|
||||
ctx.fill();
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
Polyline.prototype.isInStroke = function(ctx, view, x, y) {
|
||||
@@ -460,7 +429,7 @@ export let Polyline = (function() {
|
||||
|
||||
let pointXY = [];
|
||||
for (var j = 0; j < this.radecArray.length; j++) {
|
||||
var xy = view.aladin.world2pix(this.radecArray[j][0], this.radecArray[j][1]);
|
||||
var xy = AladinUtils.radecToViewXy(this.radecArray[j][0], this.radecArray[j][1], view.aladin);
|
||||
if (!xy) {
|
||||
return false;
|
||||
}
|
||||
@@ -472,8 +441,8 @@ export let Polyline = (function() {
|
||||
|
||||
const lastPointIdx = pointXY.length - 1;
|
||||
for (var l = 0; l < lastPointIdx; l++) {
|
||||
const line = {x1: pointXY[l].x, y1: pointXY[l].y, x2: pointXY[l + 1].x, y2: pointXY[l + 1].y}; // new segment
|
||||
_drawLine(line, ctx, true);
|
||||
const line = new Line(pointXY[l].x, pointXY[l].y, pointXY[l + 1].x, pointXY[l + 1].y); // new segment
|
||||
line.draw(ctx, true);
|
||||
|
||||
if (ctx.isPointInStroke(x, y)) { // x,y is on line?
|
||||
return true;
|
||||
@@ -481,8 +450,8 @@ export let Polyline = (function() {
|
||||
}
|
||||
|
||||
if(this.closed) {
|
||||
const line = {x1: pointXY[lastPointIdx].x, y1: pointXY[lastPointIdx].y, x2: pointXY[0].x, y2: pointXY[0].y}; // new segment
|
||||
_drawLine(line, ctx, true);
|
||||
const line = new Line(pointXY[lastPointIdx].x, pointXY[lastPointIdx].y, pointXY[0].x, pointXY[0].y); // new segment
|
||||
line.draw(ctx, true);
|
||||
|
||||
if (ctx.isPointInStroke(x, y)) { // x,y is on line?
|
||||
return true;
|
||||
@@ -496,44 +465,5 @@ export let Polyline = (function() {
|
||||
// todo
|
||||
};
|
||||
|
||||
// static methods
|
||||
// Method for testing whether a line is inside the view
|
||||
// http://www.jeffreythompson.org/collision-detection/line-rect.php
|
||||
Polyline.isInsideView = function(x1, y1, x2, y2, rw, rh) {
|
||||
if (x1 >= 0 && x1 <= rw && y1 >= 0 && y1 <= rh) {
|
||||
return true;
|
||||
}
|
||||
if (x2 >= 0 && x2 <= rw && y2 >= 0 && y2 <= rh) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// check if the line has hit any of the rectangle's sides
|
||||
// uses the Line/Line function below
|
||||
let left = Polyline._intersectLine(x1, y1, x2, y2, 0, 0, 0, rh);
|
||||
let right = Polyline._intersectLine(x1, y1, x2, y2, rw, 0, rw, rh);
|
||||
let top = Polyline._intersectLine(x1, y1, x2, y2, 0, 0, rw, 0);
|
||||
let bottom = Polyline._intersectLine(x1, y1, x2, y2, 0, rh, rw, rh);
|
||||
|
||||
// if ANY of the above are true, the line
|
||||
// has hit the rectangle
|
||||
if (left || right || top || bottom) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
Polyline._intersectLine = function(x1, y1, x2, y2, x3, y3, x4, y4) {
|
||||
// Calculate the direction of the lines
|
||||
let uA = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
|
||||
let uB = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
|
||||
|
||||
// If uA and uB are between 0-1, lines are colliding
|
||||
if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
return Polyline;
|
||||
})();
|
||||
|
||||
@@ -90,6 +90,7 @@ export let Popup = (function() {
|
||||
}
|
||||
source.popup = this;
|
||||
this.source = source;
|
||||
|
||||
this.setPosition(source.x, source.y);
|
||||
};
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ import { Color } from "./Color.js";
|
||||
import { Coo } from "./libs/astro/coo.js";
|
||||
import { Utils } from "./Utils";
|
||||
import { CooFrameEnum } from "./CooFrameEnum.js";
|
||||
|
||||
// TODO: index sources according to their HEALPix ipix
|
||||
// TODO : merge parsing with class Catalog
|
||||
export let ProgressiveCat = (function() {
|
||||
@@ -73,11 +74,20 @@ export let ProgressiveCat = (function() {
|
||||
|
||||
this.onClick = options.onClick || undefined; // TODO: inherit from catalog
|
||||
|
||||
|
||||
|
||||
// we cache the list of sources in each healpix tile. Key of the cache is norder+'-'+npix
|
||||
this.sourcesCache = new Utils.LRUCache(256);
|
||||
this.footprintsCache = new Utils.LRUCache(256);
|
||||
|
||||
//added to allow hips catalogue to also use shape functions
|
||||
if (this.shape instanceof Image || this.shape instanceof HTMLCanvasElement) {
|
||||
this.sourceSize = this.shape.width;
|
||||
}
|
||||
this._shapeIsFunction = false; // if true, the shape is a function drawing on the canvas
|
||||
if (typeof this.shape === 'function') {
|
||||
this._shapeIsFunction = true;
|
||||
}
|
||||
|
||||
this.updateShape(options);
|
||||
|
||||
this.maxOrderAllsky = 2;
|
||||
@@ -209,9 +219,7 @@ export let ProgressiveCat = (function() {
|
||||
sources.push(newSource);
|
||||
newSource.setCatalog(instance);
|
||||
}
|
||||
|
||||
let footprints = instance.computeFootprints(sources);
|
||||
return [sources, footprints];
|
||||
return sources;
|
||||
};
|
||||
|
||||
ProgressiveCat.prototype = {
|
||||
@@ -264,10 +272,7 @@ export let ProgressiveCat = (function() {
|
||||
url: self.rootUrl + '/' + 'Norder1/Allsky.tsv',
|
||||
method: 'GET',
|
||||
success: function(tsv) {
|
||||
let [sources, footprints] = getSources(self, tsv, self.fields);
|
||||
|
||||
self.order1Footprints = footprints;
|
||||
self.order1Sources = sources;
|
||||
self.order1Sources = getSources(self, tsv, self.fields);
|
||||
|
||||
if (self.order2Sources) {
|
||||
self.isReady = true;
|
||||
@@ -284,10 +289,7 @@ export let ProgressiveCat = (function() {
|
||||
url: self.rootUrl + '/' + 'Norder2/Allsky.tsv',
|
||||
method: 'GET',
|
||||
success: function(tsv) {
|
||||
let [sources, footprints] = getSources(self, tsv, self.fields);
|
||||
|
||||
self.order2Footprints = footprints;
|
||||
self.order2Sources = sources;
|
||||
self.order2Sources = getSources(self, tsv, self.fields);
|
||||
|
||||
if (self.order1Sources) {
|
||||
self.isReady = true;
|
||||
@@ -317,12 +319,7 @@ export let ProgressiveCat = (function() {
|
||||
let xml = ProgressiveCat.parser.parseFromString(text, "text/xml")
|
||||
|
||||
self.fields = getFields(self, xml);
|
||||
|
||||
let [sources, footprints] = getSources(self, xml.querySelectorAll('CSV').innerText, self.fields);
|
||||
|
||||
self.order2Footprints = footprints
|
||||
self.order2Sources = sources
|
||||
|
||||
self.order2Sources = getSources(self, xml.querySelectorAll('CSV').innerText, self.fields);
|
||||
if (self.order3Sources) {
|
||||
self.isReady = true;
|
||||
self._finishInitWhenReady();
|
||||
@@ -342,10 +339,7 @@ export let ProgressiveCat = (function() {
|
||||
method: 'GET',
|
||||
success: function(text) {
|
||||
let xml = ProgressiveCat.parser.parseFromString(text, "text/xml")
|
||||
let [sources, footprints] = getSources(self, xml.querySelectorAll('CSV').innerText, self.fields);
|
||||
self.order3Footprints = footprints
|
||||
self.order3Sources = sources
|
||||
|
||||
self.order3Sources = getSources(self, xml.querySelectorAll('CSV').innerText, self.fields);
|
||||
if (self.order2Sources) {
|
||||
self.isReady = true;
|
||||
self._finishInitWhenReady();
|
||||
@@ -362,7 +356,7 @@ export let ProgressiveCat = (function() {
|
||||
this.loadNeededTiles();
|
||||
},
|
||||
|
||||
draw: function(ctx, width, height) {
|
||||
draw: function(ctx, frame, width, height, largestDim) {
|
||||
if (! this.isShowing || ! this.isReady) {
|
||||
return;
|
||||
}
|
||||
@@ -372,67 +366,38 @@ export let ProgressiveCat = (function() {
|
||||
}
|
||||
|
||||
// Order must be >= 0
|
||||
if (this.order1Sources) {
|
||||
this.drawSources(this.order1Sources, ctx, width, height);
|
||||
}
|
||||
if (this.order1Footprints) {
|
||||
this.order1Footprints.forEach((f) => {
|
||||
f.draw(ctx, this.view)
|
||||
});
|
||||
}
|
||||
this.drawSources(this.order1Sources, ctx, width, height);
|
||||
|
||||
if (this.view.realNorder >= 1) {
|
||||
if (this.order2Sources) {
|
||||
this.drawSources(this.order2Sources, ctx, width, height);
|
||||
}
|
||||
if (this.order2Footprints) {
|
||||
this.order2Footprints.forEach((f) => {
|
||||
f.draw(ctx, this.view)
|
||||
});
|
||||
}
|
||||
this.drawSources(this.order2Sources, ctx, width, height);
|
||||
}
|
||||
|
||||
// For old allsky, tilesInView refers to tiles at orders 4..
|
||||
// For new allsky, tilesInView will contains order3 sources
|
||||
if (this.maxOrderAllsky === 3) {
|
||||
if (this.view.realNorder >= 2) {
|
||||
if (this.order3Sources) {
|
||||
this.drawSources(this.order3Sources, ctx, width, height);
|
||||
}
|
||||
if (this.order3Footprints) {
|
||||
this.order3Footprints.forEach((f) => {
|
||||
f.draw(ctx, this.view)
|
||||
});
|
||||
}
|
||||
this.drawSources(this.order3Sources, ctx, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
let key, sources, footprints;
|
||||
let key, sources;
|
||||
this.tilesInView.forEach((tile) => {
|
||||
key = tile[0] + '-' + tile[1];
|
||||
sources = this.sourcesCache.get(key);
|
||||
footprints = this.footprintsCache.get(key);
|
||||
|
||||
if (sources) {
|
||||
this.drawSources(sources, ctx, width, height);
|
||||
}
|
||||
|
||||
if (footprints) {
|
||||
footprints.forEach((f) => {
|
||||
f.draw(ctx, this.view)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (this._shapeIsFunction) {
|
||||
ctx.restore();
|
||||
}
|
||||
},
|
||||
|
||||
drawSources: function(sources, ctx, width, height) {
|
||||
let ra = []
|
||||
let dec = [];
|
||||
|
||||
if (!sources) {
|
||||
return;
|
||||
}
|
||||
let ra = [], dec = [];
|
||||
sources.forEach((s) => {
|
||||
ra.push(s.ra);
|
||||
dec.push(s.dec);
|
||||
@@ -480,32 +445,7 @@ export let ProgressiveCat = (function() {
|
||||
return ret;
|
||||
},
|
||||
|
||||
getFootprints: function() {
|
||||
var ret = [];
|
||||
if (this.order1Footprints) {
|
||||
ret = ret.concat(this.order1Footprints);
|
||||
}
|
||||
if (this.order2Footprints) {
|
||||
ret = ret.concat(this.order2Footprints);
|
||||
}
|
||||
if (this.order3Footprints) {
|
||||
ret = ret.concat(this.order3Footprints);
|
||||
}
|
||||
if (this.tilesInView) {
|
||||
var footprints, key, t;
|
||||
for (var k=0; k < this.tilesInView.length; k++) {
|
||||
t = this.tilesInView[k];
|
||||
key = t[0] + '-' + t[1];
|
||||
footprints = this.footprintsCache.get(key);
|
||||
|
||||
if (footprints) {
|
||||
ret = ret.concat(footprints);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
},
|
||||
|
||||
deselectAll: function() {
|
||||
if (this.order1Sources) {
|
||||
@@ -534,11 +474,6 @@ export let ProgressiveCat = (function() {
|
||||
for (var k=0; k<sources.length; k++) {
|
||||
sources[k].deselect();
|
||||
}
|
||||
|
||||
var footprints = this.footprintsCache[key];
|
||||
for (var k=0; k<footprints.length; k++) {
|
||||
footprints[k].deselect();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -566,6 +501,11 @@ export let ProgressiveCat = (function() {
|
||||
return this.rootUrl + "/" + "Norder" + norder + "/Dir" + dirIdx + "/Npix" + npix + ".tsv";
|
||||
},
|
||||
|
||||
// todo, allow HiPS cats to support footprints
|
||||
getFootprints: function() {
|
||||
return null;
|
||||
},
|
||||
|
||||
loadNeededTiles: function() {
|
||||
if ( ! this.isShowing) {
|
||||
return;
|
||||
@@ -619,23 +559,18 @@ export let ProgressiveCat = (function() {
|
||||
url: Aladin.JSONP_PROXY,
|
||||
data: {"url": self.getTileURL(norder, ipix)},
|
||||
*/
|
||||
// ATTENTION : je passe en JSON direct, car je n'arrive pas a choper les 404 en JSONP
|
||||
// ATTENTIOn : je passe en JSON direct, car je n'arrive pas a choper les 404 en JSONP
|
||||
url: self.getTileURL(norder, ipix),
|
||||
desc: "Get tile .tsv " + norder + ' ' + ipix + ' of ' + self.name,
|
||||
method: 'GET',
|
||||
//dataType: 'jsonp',
|
||||
success: function(tsv) {
|
||||
let [sources, footprints] = getSources(self, tsv, self.fields);
|
||||
|
||||
self.sourcesCache.set(key, sources);
|
||||
self.footprintsCache.set(key, footprints);
|
||||
|
||||
self.sourcesCache.set(key, getSources(self, tsv, self.fields));
|
||||
self.view.requestRedraw();
|
||||
},
|
||||
error: function() {
|
||||
// on suppose qu'il s'agit d'une erreur 404
|
||||
self.sourcesCache.set(key, []);
|
||||
self.footprintsCache.set(key, []);
|
||||
}
|
||||
});
|
||||
})(this, t[0], t[1]);
|
||||
@@ -643,8 +578,6 @@ export let ProgressiveCat = (function() {
|
||||
}
|
||||
},
|
||||
|
||||
computeFootprints: Catalog.prototype.computeFootprints,
|
||||
|
||||
reportChange: function() { // TODO: to be shared with Catalog
|
||||
this.view && this.view.requestRedraw();
|
||||
}
|
||||
|
||||
@@ -99,14 +99,13 @@ export class Selector {
|
||||
if (view.catalogs) {
|
||||
for (var k = 0; k < view.catalogs.length; k++) {
|
||||
cat = view.catalogs[k];
|
||||
|
||||
if (!cat.isShowing) {
|
||||
continue;
|
||||
}
|
||||
sources = cat.getSources();
|
||||
for (var l = 0; l < sources.length; l++) {
|
||||
s = sources[l];
|
||||
if (!s.isShowing || !s.x || !s.y || s.tooSmallFootprint === false) {
|
||||
if (!s.isShowing || !s.x || !s.y) {
|
||||
continue;
|
||||
}
|
||||
if (selection.contains(s)) {
|
||||
|
||||
@@ -62,7 +62,7 @@ export let SimbadPointer = (function() {
|
||||
}
|
||||
content += '<br><a target="_blank" href="http://cdsportal.u-strasbg.fr/?target=' + encodeURIComponent(objName) + '">Query in CDS portal</a>';
|
||||
content += '</div>';
|
||||
|
||||
|
||||
aladinInstance.showPopup(objCoo.lon, objCoo.lat, title, content);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -117,12 +117,7 @@ export let Source = (function() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulates a click on the source
|
||||
*
|
||||
* @memberof Source
|
||||
* @param {Footprint|Source} [obj] - If not given, the source is taken as the object to be selected
|
||||
*/
|
||||
// function called when a source is clicked. Called by the View object
|
||||
Source.prototype.actionClicked = function(obj) {
|
||||
if (this.catalog && this.catalog.onClick) {
|
||||
var view = this.catalog.view;
|
||||
|
||||
@@ -125,39 +125,6 @@ Utils.isInt = function (n: string | number) {
|
||||
return Utils.isNumber(n) && Math.floor(n as number) === n
|
||||
}
|
||||
|
||||
// Newton-Raphson method to find the approximate inverse of f(x)
|
||||
Utils.inverseNewtonRaphson = function(y: number, f: Function, fPrime: Function, tolerance=1e-6, maxIterations=100) {
|
||||
let x_guess = 0.5; // Initial guess
|
||||
let iteration = 0;
|
||||
|
||||
while (iteration < maxIterations) {
|
||||
let f_x = f(x_guess);
|
||||
let error = Math.abs(f_x - y);
|
||||
|
||||
if (error < tolerance) {
|
||||
return x_guess; // Found approximate inverse
|
||||
}
|
||||
|
||||
let derivative = fPrime(x_guess);
|
||||
x_guess = x_guess - (f_x - y) / derivative; // Newton-Raphson update
|
||||
iteration++;
|
||||
}
|
||||
|
||||
return null; // No convergence within maxIterations
|
||||
}
|
||||
|
||||
Utils.binarySearch = function(array, value) {
|
||||
var low = 0,
|
||||
high = array.length;
|
||||
|
||||
while (low < high) {
|
||||
var mid = (low + high) >>> 1;
|
||||
if (array[mid] > value) low = mid + 1;
|
||||
else high = mid;
|
||||
}
|
||||
return low;
|
||||
}
|
||||
|
||||
/* a debounce function, used to prevent multiple calls to the same function if less than delay milliseconds have passed */
|
||||
Utils.debounce = function (fn, delay) {
|
||||
var timer = null
|
||||
@@ -265,7 +232,7 @@ Utils.loadFromUrls = function (urls, options) {
|
||||
const mode = options && options.mode || 'cors';
|
||||
const contentType = options && options.contentType || undefined;
|
||||
const dataType = (options && options.dataType) || 'text';
|
||||
const desc = options && options.desc;
|
||||
|
||||
// A controller that can abort the query when a timeout is reached
|
||||
const controller = new AbortController()
|
||||
|
||||
@@ -289,9 +256,7 @@ Utils.loadFromUrls = function (urls, options) {
|
||||
cache: 'default',
|
||||
// Abort the request when a timeout exceeded
|
||||
signal: controller.signal,
|
||||
dataType,
|
||||
// message description
|
||||
desc
|
||||
dataType
|
||||
}
|
||||
|
||||
if (contentType) {
|
||||
@@ -345,24 +310,17 @@ Utils.getAjaxObject = function (url, method, dataType, useProxy) {
|
||||
*/
|
||||
|
||||
Utils.fetch = function(params) {
|
||||
let url;
|
||||
try {
|
||||
url = new URL(params.url);
|
||||
if (params.useProxy === true) {
|
||||
url = Utils.handleCORSNotSameOrigin(url)
|
||||
}
|
||||
|
||||
if (params.data) {
|
||||
// add the search params to the url object
|
||||
for (const key in params.data) {
|
||||
url.searchParams.append(key, params.data[key]);
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
// localhost url
|
||||
url = params.url;
|
||||
let url = new URL(params.url);
|
||||
if (params.useProxy === true) {
|
||||
url = Utils.handleCORSNotSameOrigin(url)
|
||||
}
|
||||
|
||||
if (params.data) {
|
||||
// add the search params to the url object
|
||||
for (const key in params.data) {
|
||||
url.searchParams.append(key, params.data[key]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let request = new Request(url, {
|
||||
method: params.method || 'GET',
|
||||
@@ -389,15 +347,13 @@ Utils.fetch = function(params) {
|
||||
if (params.success) {
|
||||
return params.success(data)
|
||||
}
|
||||
|
||||
return Promise.resolve(data);
|
||||
})
|
||||
.catch(e => {
|
||||
if (params.error) {
|
||||
return params.error(e)
|
||||
params.error(e)
|
||||
} else {
|
||||
alert(e)
|
||||
}
|
||||
|
||||
return Promise.reject(e);
|
||||
})
|
||||
.finally(() => {
|
||||
ALEvent.RESOURCE_FETCHED.dispatchedTo(document, {task});
|
||||
@@ -473,14 +429,6 @@ Utils.fixURLForHTTPS = function (url) {
|
||||
return url
|
||||
}
|
||||
|
||||
Utils.isUrl = function(url) {
|
||||
try {
|
||||
return new URL(url).href;
|
||||
} catch(e) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// generate an absolute URL from a relative URL
|
||||
// example: getAbsoluteURL('foo/bar/toto') return http://cds.unistra.fr/AL/foo/bar/toto if executed from page http://cds.unistra.fr/AL/
|
||||
Utils.getAbsoluteURL = function (url) {
|
||||
|
||||
438
src/js/View.js
438
src/js/View.js
@@ -43,14 +43,15 @@ import { CooFrameEnum } from "./CooFrameEnum.js";
|
||||
import { requestAnimFrame } from "./libs/RequestAnimationFrame.js";
|
||||
import { WebGLCtx } from "./WebGL.js";
|
||||
import { ALEvent } from "./events/ALEvent.js";
|
||||
import { Zoom } from './Zoom.js'
|
||||
import { ColorCfg } from "./ColorCfg.js";
|
||||
import { Footprint } from "./Footprint.js";
|
||||
import { Selector } from "./Selector.js";
|
||||
import { ObsCore } from "./vo/ObsCore.js";
|
||||
import { DefaultActionsForContextMenu } from "./DefaultActionsForContextMenu.js";
|
||||
import { Layout } from "./gui/Layout.js";
|
||||
import { SAMPActionButton } from "./gui/Button/SAMP.js";
|
||||
import { HiPSCache } from "./DefaultHiPSCache.js";
|
||||
import { ImageSurvey } from "./ImageSurvey.js";
|
||||
import { ImageFITS } from "./ImageFITS.js";
|
||||
|
||||
export let View = (function () {
|
||||
|
||||
@@ -120,42 +121,6 @@ export let View = (function () {
|
||||
|
||||
this.aladinDiv.ondragover = Utils.dragOverHandler;
|
||||
|
||||
this.throttledPositionChanged = Utils.throttle(
|
||||
() => {
|
||||
var posChangedFn = this.aladin.callbacksByEventName && this.aladin.callbacksByEventName['positionChanged'];
|
||||
if (typeof posChangedFn === 'function') {
|
||||
var pos = this.aladin.pix2world(this.width / 2, this.height / 2, 'icrs');
|
||||
if (pos !== undefined) {
|
||||
try {
|
||||
posChangedFn({
|
||||
ra: pos[0],
|
||||
dec: pos[1],
|
||||
dragging: true
|
||||
});
|
||||
} catch(e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
View.CALLBACKS_THROTTLE_TIME_MS,
|
||||
);
|
||||
|
||||
this.throttledZoomChanged = Utils.throttle(
|
||||
() => {
|
||||
const fov = this.fov;
|
||||
// trigger callback only if FoV (zoom) has changed !
|
||||
if (fov !== this.oldFov) {
|
||||
const fovChangedFn = this.aladin.callbacksByEventName['zoomChanged'];
|
||||
(typeof fovChangedFn === 'function') && fovChangedFn(fov);
|
||||
|
||||
// finally, save fov value
|
||||
this.oldFov = fov;
|
||||
}
|
||||
},
|
||||
View.CALLBACKS_THROTTLE_TIME_MS,
|
||||
);
|
||||
|
||||
this.mustClearCatalog = true;
|
||||
this.mode = View.PAN;
|
||||
|
||||
@@ -168,11 +133,14 @@ export let View = (function () {
|
||||
lon = lat = 0;
|
||||
|
||||
// FoV init settings
|
||||
const si = 500000.0;
|
||||
const alpha = 40.0;
|
||||
let initialFov = this.options.fov || 180.0;
|
||||
this.pinchZoomParameters = {
|
||||
isPinching: false, // true if a pinch zoom is ongoing
|
||||
initialFov: undefined,
|
||||
initialDistance: undefined,
|
||||
initialAccDelta: Math.pow(si / initialFov, 1.0 / alpha)
|
||||
};
|
||||
|
||||
// Projection definition
|
||||
@@ -180,8 +148,7 @@ export let View = (function () {
|
||||
this.setProjection(projName)
|
||||
|
||||
// Then set the zoom properly once the projection is defined
|
||||
this.wasm.setFieldOfView(initialFov);
|
||||
this.updateZoomState();
|
||||
this.setZoom(initialFov);
|
||||
|
||||
// Target position settings
|
||||
this.viewCenter = { lon, lat }; // position of center of view
|
||||
@@ -249,7 +216,6 @@ export let View = (function () {
|
||||
initialFingerAngle: undefined,
|
||||
rotationInitiated: false
|
||||
}
|
||||
this.zoom = new Zoom(this);
|
||||
|
||||
this.fadingLatestUpdate = null;
|
||||
this.dateRequestRedraw = null;
|
||||
@@ -262,13 +228,51 @@ export let View = (function () {
|
||||
self.fixLayoutDimensions();
|
||||
})
|
||||
} else {*/
|
||||
|
||||
this.resizeObserver = new ResizeObserver(() => {
|
||||
let resizeLayout = () => {
|
||||
self.fixLayoutDimensions();
|
||||
}
|
||||
|
||||
let doit;
|
||||
this.resizeObserver = new ResizeObserver(() => {
|
||||
//clearTimeout(doit);
|
||||
//doit = setTimeout(resizeLayout, 100);
|
||||
resizeLayout();
|
||||
});
|
||||
|
||||
self.resizeObserver.observe(this.aladinDiv)
|
||||
|
||||
this.throttledPositionChanged = Utils.throttle(
|
||||
() => {
|
||||
var posChangedFn = this.aladin.callbacksByEventName && this.aladin.callbacksByEventName['positionChanged'];
|
||||
if (typeof posChangedFn === 'function') {
|
||||
var pos = this.aladin.pix2world(this.width / 2, this.height / 2);
|
||||
if (pos !== undefined) {
|
||||
posChangedFn({
|
||||
ra: pos[0],
|
||||
dec: pos[1],
|
||||
dragging: true
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
View.CALLBACKS_THROTTLE_TIME_MS,
|
||||
);
|
||||
|
||||
this.throttledZoomChanged = Utils.throttle(
|
||||
() => {
|
||||
const fov = this.fov;
|
||||
// trigger callback only if FoV (zoom) has changed !
|
||||
if (fov !== this.oldFov) {
|
||||
const fovChangedFn = this.aladin.callbacksByEventName['zoomChanged'];
|
||||
(typeof fovChangedFn === 'function') && fovChangedFn(fov);
|
||||
|
||||
// finally, save fov value
|
||||
this.oldFov = fov;
|
||||
}
|
||||
},
|
||||
View.CALLBACKS_THROTTLE_TIME_MS,
|
||||
);
|
||||
|
||||
self.fixLayoutDimensions();
|
||||
self.redraw()
|
||||
|
||||
@@ -312,10 +316,10 @@ export let View = (function () {
|
||||
imageCanvas.remove();
|
||||
}
|
||||
|
||||
/*let gridCanvas = this.aladinDiv.querySelector('.aladin-gridCanvas');
|
||||
let gridCanvas = this.aladinDiv.querySelector('.aladin-gridCanvas');
|
||||
if (gridCanvas) {
|
||||
gridCanvas.remove();
|
||||
}*/
|
||||
}
|
||||
|
||||
let catalogCanvas = this.aladinDiv.querySelector('.aladin-catalogCanvas')
|
||||
if (catalogCanvas) {
|
||||
@@ -335,7 +339,7 @@ export let View = (function () {
|
||||
};
|
||||
|
||||
this.catalogCanvas = createCanvas('aladin-catalogCanvas');
|
||||
//this.gridCanvas = createCanvas('aladin-gridCanvas');
|
||||
this.gridCanvas = createCanvas('aladin-gridCanvas');
|
||||
this.imageCanvas = createCanvas('aladin-imageCanvas');
|
||||
};
|
||||
|
||||
@@ -365,16 +369,13 @@ export let View = (function () {
|
||||
// reinitialize 2D context
|
||||
|
||||
this.catalogCtx = this.catalogCanvas.getContext("2d");
|
||||
this.catalogCtx.canvas.width = this.width * window.devicePixelRatio;
|
||||
this.catalogCtx.canvas.height = this.height * window.devicePixelRatio;
|
||||
this.catalogCtx.canvas.style.width = this.width + "px";
|
||||
this.catalogCtx.canvas.style.height = this.height + "px";
|
||||
this.catalogCtx.scale(window.devicePixelRatio, window.devicePixelRatio);
|
||||
this.catalogCtx.canvas.width = this.width;
|
||||
this.catalogCtx.canvas.height = this.height;
|
||||
|
||||
/*this.gridCtx = this.gridCanvas.getContext("2d");
|
||||
this.gridCtx = this.gridCanvas.getContext("2d");
|
||||
this.gridCtx.canvas.width = this.width;
|
||||
this.gridCtx.canvas.height = this.height;
|
||||
*/
|
||||
|
||||
this.imageCtx = this.imageCanvas.getContext("webgl2");
|
||||
this.imageCtx.canvas.style.width = this.width + "px";
|
||||
this.imageCtx.canvas.style.height = this.height + "px";
|
||||
@@ -466,8 +467,9 @@ export let View = (function () {
|
||||
const canvas = this.wasm.canvas();
|
||||
|
||||
var c = document.createElement('canvas');
|
||||
c.width = width || (this.width * window.devicePixelRatio);
|
||||
c.height = height || (this.height * window.devicePixelRatio);
|
||||
let dpi = window.devicePixelRatio;
|
||||
c.width = width || (this.width * dpi);
|
||||
c.height = height || (this.height * dpi);
|
||||
|
||||
var ctx = c.getContext('2d');
|
||||
|
||||
@@ -485,10 +487,8 @@ export let View = (function () {
|
||||
|
||||
View.prototype.selectLayer = function (layer) {
|
||||
if (!this.imageLayers.has(layer)) {
|
||||
console.warn(layer + ' does not exists. So cannot be selected');
|
||||
return;
|
||||
throw layer + ' does not exists. So cannot be selected';
|
||||
}
|
||||
|
||||
this.selectedLayer = layer;
|
||||
};
|
||||
|
||||
@@ -506,8 +506,9 @@ export let View = (function () {
|
||||
view.unselectObjects()
|
||||
|
||||
try {
|
||||
const [lon, lat] = view.aladin.pix2world(xymouse.x, xymouse.y, 'icrs');
|
||||
view.pointTo(lon, lat);
|
||||
const lonlat = view.wasm.screenToWorld(xymouse.x, xymouse.y);
|
||||
var radec = view.wasm.viewToICRSCooSys(lonlat[0], lonlat[1]);
|
||||
view.pointTo(radec[0], radec[1]);
|
||||
}
|
||||
catch (err) {
|
||||
return;
|
||||
@@ -663,6 +664,8 @@ export let View = (function () {
|
||||
view.dragging = false;
|
||||
|
||||
view.pinchZoomParameters.isPinching = true;
|
||||
//var fov = view.aladin.getFov();
|
||||
//view.pinchZoomParameters.initialFov = Math.max(fov[0], fov[1]);
|
||||
var fov = view.wasm.getFieldOfView();
|
||||
view.pinchZoomParameters.initialFov = fov;
|
||||
view.pinchZoomParameters.initialDistance = Math.sqrt(Math.pow(e.targetTouches[0].clientX - e.targetTouches[1].clientX, 2) + Math.pow(e.targetTouches[0].clientY - e.targetTouches[1].clientY, 2));
|
||||
@@ -1058,21 +1061,13 @@ export let View = (function () {
|
||||
var eventCount = 0;
|
||||
var eventCountStart;
|
||||
var isTouchPad;
|
||||
let id;
|
||||
|
||||
var scale = 0.0;
|
||||
Utils.on(view.catalogCanvas, 'wheel', function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
view.wheelTriggered = true;
|
||||
|
||||
clearTimeout(id);
|
||||
id = setTimeout(() => {
|
||||
view.wheelTriggered = false;
|
||||
}, 100);
|
||||
|
||||
const xymouse = Utils.relMouseCoords(e);
|
||||
view.xy = xymouse
|
||||
|
||||
ALEvent.CANVAS_EVENT.dispatchedTo(view.aladinDiv, {
|
||||
state: {
|
||||
mode: view.mode,
|
||||
@@ -1087,25 +1082,21 @@ export let View = (function () {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!view.debounceProgCatOnZoom) {
|
||||
var self = view;
|
||||
view.debounceProgCatOnZoom = Utils.debounce(function () {
|
||||
self.refreshProgressiveCats();
|
||||
self.drawAllOverlays();
|
||||
}, 300);
|
||||
}
|
||||
var delta = e.deltaY || e.detail || (-e.wheelDelta);
|
||||
|
||||
view.debounceProgCatOnZoom();
|
||||
//view.throttledZoomChanged();
|
||||
// Limit the minimum and maximum zoom levels
|
||||
//var delta = e.deltaY;
|
||||
// this seems to happen in context of Jupyter notebook --> we have to invert the direction of scroll
|
||||
// hope this won't trigger some side effects ...
|
||||
/*if (e.hasOwnProperty('originalEvent')) {
|
||||
delta = -e.deltaY;
|
||||
}*/
|
||||
|
||||
// Zoom heuristic
|
||||
// First detect the device
|
||||
// See https://stackoverflow.com/questions/10744645/detect-touchpad-vs-mouse-in-javascript
|
||||
// for detecting the use of a touchpad
|
||||
view.isTouchPadDefined = isTouchPad || typeof isTouchPad !== "undefined";
|
||||
if (!view.isTouchPadDefined) {
|
||||
var isTouchPadDefined = isTouchPad || typeof isTouchPad !== "undefined";
|
||||
if (!isTouchPadDefined) {
|
||||
if (eventCount === 0) {
|
||||
view.delta = 0;
|
||||
eventCountStart = new Date().getTime();
|
||||
}
|
||||
|
||||
@@ -1117,78 +1108,51 @@ export let View = (function () {
|
||||
} else {
|
||||
isTouchPad = false;
|
||||
}
|
||||
view.isTouchPadDefined = true;
|
||||
isTouchPadDefined = true;
|
||||
}
|
||||
}
|
||||
|
||||
// only ensure the touch pad test has been done before zooming
|
||||
if (!view.isTouchPadDefined) {
|
||||
return false;
|
||||
}
|
||||
// The value of the field of view is determined
|
||||
// inside the backend
|
||||
const triggerZoom = (amount) => {
|
||||
if (delta < 0.0) {
|
||||
view.increaseZoom(amount);
|
||||
} else {
|
||||
view.decreaseZoom(amount);
|
||||
}
|
||||
};
|
||||
|
||||
// touch pad defined
|
||||
view.delta = e.deltaY || e.detail || (-e.wheelDelta);
|
||||
if (isTouchPadDefined) {
|
||||
let dt = performance.now() - view.then
|
||||
|
||||
if (isTouchPad) {
|
||||
if (!view.throttledTouchPadZoom) {
|
||||
let radec;
|
||||
view.throttledTouchPadZoom = Utils.throttle(() => {
|
||||
if (!view.zoom.isZooming && !view.wheelTriggered) {
|
||||
// start zooming detected
|
||||
radec = view.aladin.pix2world(view.xy.x, view.xy.y);
|
||||
}
|
||||
|
||||
let amount = view.delta > 0 ? -Zoom.MAX_IDX_DELTA_PER_TROTTLE : Zoom.MAX_IDX_DELTA_PER_TROTTLE;
|
||||
if (amount === 0)
|
||||
return;
|
||||
|
||||
// change the zoom level
|
||||
let newFov = Zoom.determineNextFov(view, amount);
|
||||
view.zoom.apply({
|
||||
stop: newFov,
|
||||
duration: 300
|
||||
});
|
||||
//view.setZoom(newFov)
|
||||
|
||||
/*if (amount > 0 && radec) {
|
||||
let sRaDec = view.aladin.getRaDec();
|
||||
|
||||
let moveTo = function() {
|
||||
const t = 1 - (view.x - view.x1) / (view.x2 - view.x1);
|
||||
|
||||
let ra = (0.5 + t*0.5) * sRaDec[0] + (0.5 - t*0.5) * radec[0]
|
||||
let dec = (0.5 + t*0.5) * sRaDec[1] + (0.5 - t*0.5) * radec[1]
|
||||
|
||||
view.aladin.gotoRaDec(ra, dec)
|
||||
|
||||
if (t >= 1e-2)
|
||||
requestAnimFrame(moveTo)
|
||||
}
|
||||
//requestAnimFrame(moveTo)
|
||||
}*/
|
||||
}, 40);
|
||||
let a0, a1;
|
||||
|
||||
// touchpad
|
||||
if (isTouchPad) {
|
||||
a1 = 0.002;
|
||||
a0 = 0.0002;
|
||||
} else {
|
||||
a1 = 0.01;
|
||||
a0 = 0.0004;
|
||||
}
|
||||
|
||||
view.throttledTouchPadZoom();
|
||||
} else {
|
||||
if (!view.throttledMouseScrollZoom) {
|
||||
view.throttledMouseScrollZoom = Utils.throttle(() => {
|
||||
const factor = 5
|
||||
let newFov = view.delta > 0 ? view.fov * factor : view.fov / factor;
|
||||
// standard mouse wheel zooming
|
||||
|
||||
newFov = Math.max(Math.min(newFov, Zoom.MAX), Zoom.MIN)
|
||||
|
||||
view.zoom.apply({
|
||||
stop: newFov,
|
||||
duration: 300
|
||||
});
|
||||
}, 30);
|
||||
}
|
||||
|
||||
view.throttledMouseScrollZoom()
|
||||
const alpha = Math.pow(view.fov / view.projection.fov, 0.5);
|
||||
|
||||
const lerp = a0 * alpha + a1 * (1.0 - alpha);
|
||||
triggerZoom(lerp);
|
||||
}
|
||||
|
||||
if (!view.debounceProgCatOnZoom) {
|
||||
var self = view;
|
||||
view.debounceProgCatOnZoom = Utils.debounce(function () {
|
||||
self.refreshProgressiveCats();
|
||||
self.drawAllOverlays();
|
||||
}, 300);
|
||||
}
|
||||
|
||||
view.debounceProgCatOnZoom();
|
||||
view.throttledZoomChanged();
|
||||
|
||||
return false;
|
||||
});
|
||||
};
|
||||
@@ -1243,13 +1207,11 @@ export let View = (function () {
|
||||
// specified fpsInterval not being a multiple of RAF's interval (16.7ms)
|
||||
|
||||
// Drawing code
|
||||
//try {
|
||||
this.moving = this.wasm.update(elapsedTime);
|
||||
|
||||
// inertia run throttled position
|
||||
if (this.moving && this.aladin.callbacksByEventName && this.aladin.callbacksByEventName['positionChanged'] && this.wasm.isInerting()) {
|
||||
// run the trottled position
|
||||
this.throttledPositionChanged();
|
||||
|
||||
try {
|
||||
this.moving = this.wasm.update(elapsedTime);
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
|
||||
////// 2. Draw catalogues////////
|
||||
@@ -1268,7 +1230,6 @@ export let View = (function () {
|
||||
View.prototype.drawAllOverlays = function () {
|
||||
var ctx = this.catalogCtx;
|
||||
this.catalogCanvasCleared = false;
|
||||
|
||||
if (this.mustClearCatalog) {
|
||||
ctx.clearRect(0, 0, this.width, this.height);
|
||||
this.catalogCanvasCleared = true;
|
||||
@@ -1285,7 +1246,7 @@ export let View = (function () {
|
||||
|
||||
for (var i = 0; i < this.catalogs.length; i++) {
|
||||
var cat = this.catalogs[i];
|
||||
cat.draw(ctx, this.width, this.height);
|
||||
cat.draw(ctx, this.cooFrame, this.width, this.height, this.largestDim);
|
||||
}
|
||||
}
|
||||
// draw popup catalog
|
||||
@@ -1295,11 +1256,11 @@ export let View = (function () {
|
||||
this.catalogCanvasCleared = true;
|
||||
}
|
||||
|
||||
this.catalogForPopup.draw(ctx, this.width, this.height);
|
||||
this.catalogForPopup.draw(ctx, this.cooFrame, this.width, this.height, this.largestDim);
|
||||
|
||||
// draw popup overlay layer
|
||||
if (this.overlayForPopup.isShowing) {
|
||||
this.overlayForPopup.draw(ctx);
|
||||
this.overlayForPopup.draw(ctx, this.cooFrame, this.width, this.height, this.largestDim);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1340,22 +1301,7 @@ export let View = (function () {
|
||||
}
|
||||
}
|
||||
|
||||
// display grid labels
|
||||
if (this.gridCfg.enabled && this.gridCfg.showLabels) {
|
||||
if (!this.catalogCanvasCleared) {
|
||||
ctx.clearRect(0, 0, this.width, this.height);
|
||||
this.catalogCanvasCleared = true;
|
||||
}
|
||||
|
||||
this.wasm.drawGridLabels();
|
||||
}
|
||||
|
||||
if (this.mode === View.SELECT) {
|
||||
if (!this.catalogCanvasCleared) {
|
||||
ctx.clearRect(0, 0, this.width, this.height);
|
||||
this.catalogCanvasCleared = true;
|
||||
}
|
||||
|
||||
this.selector.dispatch('draw')
|
||||
}
|
||||
};
|
||||
@@ -1374,7 +1320,8 @@ export let View = (function () {
|
||||
|
||||
View.prototype.getVisiblePixList = function (norder) {
|
||||
var pixList = [];
|
||||
let [lon, lat] = this.aladin.pix2world(this.cx, this.cy, 'icrs');
|
||||
let centerWorldPosition = this.wasm.screenToWorld(this.cx, this.cy);
|
||||
const [lon, lat] = this.wasm.viewToICRSCooSys(centerWorldPosition[0], centerWorldPosition[1]);
|
||||
|
||||
var radius = this.fov * 0.5 * this.ratio;
|
||||
this.wasm.queryDisc(norder, lon, lat, radius).forEach(x => pixList.push(Number(x)));
|
||||
@@ -1468,6 +1415,7 @@ export let View = (function () {
|
||||
};
|
||||
|
||||
// Called for touchmove events
|
||||
// initialAccDelta must be consistent with fovDegrees here
|
||||
View.prototype.setZoom = function (fov) {
|
||||
// limit the fov in function of the projection
|
||||
fov = Math.min(fov, this.projection.fov);
|
||||
@@ -1485,41 +1433,69 @@ export let View = (function () {
|
||||
}
|
||||
|
||||
this.wasm.setFieldOfView(fov);
|
||||
|
||||
this.updateZoomState();
|
||||
};
|
||||
|
||||
View.prototype.increaseZoom = function () {
|
||||
this.zoom.apply({
|
||||
stop: Zoom.determineNextFov(this, 6),
|
||||
duration: 300
|
||||
});
|
||||
View.prototype.increaseZoom = function (amount) {
|
||||
const si = 500000.0;
|
||||
const alpha = 40.0;
|
||||
|
||||
let initialAccDelta = this.pinchZoomParameters.initialAccDelta + amount;
|
||||
let new_fov = si / Math.pow(initialAccDelta, alpha);
|
||||
|
||||
if (new_fov < 0.00002777777) {
|
||||
new_fov = 0.00002777777;
|
||||
}
|
||||
|
||||
this.pinchZoomParameters.initialAccDelta = initialAccDelta;
|
||||
this.setZoom(new_fov);
|
||||
}
|
||||
|
||||
View.prototype.decreaseZoom = function () {
|
||||
this.zoom.apply({
|
||||
stop: Zoom.determineNextFov(this, -6),
|
||||
duration: 300
|
||||
});
|
||||
View.prototype.decreaseZoom = function (amount) {
|
||||
const si = 500000.0;
|
||||
const alpha = 40.0;
|
||||
|
||||
let initialAccDelta = this.pinchZoomParameters.initialAccDelta - amount;
|
||||
|
||||
if (initialAccDelta <= 0.0) {
|
||||
initialAccDelta = 1e-3;
|
||||
}
|
||||
|
||||
let new_fov = si / Math.pow(initialAccDelta, alpha);
|
||||
|
||||
if (new_fov >= this.projection.fov) {
|
||||
new_fov = this.projection.fov;
|
||||
}
|
||||
|
||||
this.pinchZoomParameters.initialAccDelta = initialAccDelta;
|
||||
this.setZoom(new_fov);
|
||||
}
|
||||
|
||||
View.prototype.setRotation = function(rotation) {
|
||||
this.wasm.setRotationAroundCenter(rotation);
|
||||
}
|
||||
|
||||
View.prototype.setGridOptions = function (options) {
|
||||
this.gridCfg = {...this.gridCfg, ...options};
|
||||
this.wasm.setGridOptions(this.gridCfg);
|
||||
View.prototype.setGridConfig = function (gridCfg) {
|
||||
this.gridCfg = {...this.gridCfg, ...gridCfg};
|
||||
this.wasm.setGridConfig(this.gridCfg);
|
||||
|
||||
if (!this.gridCfg.enabled) {
|
||||
this.mustClearCatalog = true;
|
||||
}
|
||||
// send events
|
||||
/*if (this.gridCfg.hasOwnProperty('enabled')) {
|
||||
if (this.gridCfg.enabled === true) {
|
||||
ALEvent.COO_GRID_ENABLED.dispatchedTo(this.aladinDiv);
|
||||
}
|
||||
else {
|
||||
ALEvent.COO_GRID_DISABLED.dispatchedTo(this.aladinDiv);
|
||||
}
|
||||
}*/
|
||||
|
||||
ALEvent.COO_GRID_UPDATED.dispatchedTo(this.aladinDiv, this.gridCfg);
|
||||
|
||||
this.requestRedraw();
|
||||
};
|
||||
|
||||
View.prototype.getGridOptions = function() {
|
||||
View.prototype.getGridConfig = function() {
|
||||
return this.gridCfg;
|
||||
}
|
||||
|
||||
@@ -1527,6 +1503,11 @@ export let View = (function () {
|
||||
// Get the new zoom values from the backend
|
||||
let fov = this.wasm.getFieldOfView();
|
||||
|
||||
// Update the pinch zoom parameters consequently
|
||||
const si = 500000.0;
|
||||
const alpha = 40.0;
|
||||
this.pinchZoomParameters.initialAccDelta = Math.pow(si / fov, 1.0 / alpha);
|
||||
|
||||
// Save it
|
||||
this.fov = fov;
|
||||
this.computeNorder();
|
||||
@@ -1537,8 +1518,6 @@ export let View = (function () {
|
||||
fovY = Math.min(fovY, 180);
|
||||
|
||||
ALEvent.ZOOM_CHANGED.dispatchedTo(this.aladinDiv, { fovX: fovX, fovY: fovY });
|
||||
|
||||
this.throttledZoomChanged();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1582,20 +1561,10 @@ export let View = (function () {
|
||||
if (idxOverlayLayer == -1) {
|
||||
// it does not exist so we add it to the stack
|
||||
this.overlayLayers.push(layerName);
|
||||
} else {
|
||||
// it exists
|
||||
let alreadyPresentImageLayer = this.imageLayers.get(layerName);
|
||||
alreadyPresentImageLayer.added = false;
|
||||
}
|
||||
|
||||
imageLayer.added = true;
|
||||
this.imageLayers.set(layerName, imageLayer);
|
||||
|
||||
// select the layer if he is on top
|
||||
if (idxOverlayLayer == -1) {
|
||||
this.selectLayer(layerName);
|
||||
}
|
||||
|
||||
ALEvent.HIPS_LAYER_ADDED.dispatchedTo(this.aladinDiv, { layer: imageLayer });
|
||||
}
|
||||
|
||||
@@ -1614,7 +1583,7 @@ export let View = (function () {
|
||||
Promise.allSettled(this.promises)
|
||||
.then(() => imageLayerPromise)
|
||||
// The promise is resolved and we now have access
|
||||
// to the image layer objet (whether it is an ImageHiPS or an ImageFITS)
|
||||
// to the image layer objet (whether it is an ImageSurvey or an ImageFITS)
|
||||
.then((imageLayer) => {
|
||||
// Add to the backend
|
||||
const promise = imageLayer.add(layer);
|
||||
@@ -1643,11 +1612,8 @@ export let View = (function () {
|
||||
})
|
||||
.catch((e) => {
|
||||
// remove it from the cache
|
||||
HiPSCache.delete(imageLayer.id)
|
||||
|
||||
if (imageLayer.errorCallback) {
|
||||
imageLayer.errorCallback(e);
|
||||
}
|
||||
delete ImageSurvey.cache[imageLayer.id]
|
||||
delete ImageFITS.cache[imageLayer.id]
|
||||
|
||||
throw e;
|
||||
})
|
||||
@@ -1666,7 +1632,7 @@ export let View = (function () {
|
||||
if (noMoreLayersToWaitFor) {
|
||||
if (self.empty) {
|
||||
// no promises to launch!
|
||||
//self.aladin.setBaseImageLayer(self.aladin.createImageSurvey(ImageHiPS.DEFAULT_SURVEY_ID));
|
||||
//self.aladin.setBaseImageLayer(self.aladin.createImageSurvey(ImageSurvey.DEFAULT_SURVEY_ID));
|
||||
} else {
|
||||
// there is surveys that have been queried
|
||||
// rename the first overlay layer to "base"
|
||||
@@ -1695,6 +1661,11 @@ export let View = (function () {
|
||||
this.imageLayers.delete(layer);
|
||||
this.imageLayers.set(newLayer, imageLayer);
|
||||
|
||||
// Change the selected layer if this is the one renamed
|
||||
/*if (this.selectedLayer === layer) {
|
||||
this.selectedLayer = newLayer;
|
||||
}*/
|
||||
|
||||
// Tell the layer hierarchy has changed
|
||||
ALEvent.HIPS_LAYER_RENAMED.dispatchedTo(this.aladinDiv, { layer, newLayer });
|
||||
}
|
||||
@@ -1745,8 +1716,8 @@ export let View = (function () {
|
||||
if (this.overlayLayers.length === 0) {
|
||||
this.empty = true;
|
||||
} else if (this.selectedLayer === layer) {
|
||||
// If the layer removed was selected then we select the last layer
|
||||
this.selectLayer(this.overlayLayers[this.overlayLayers.length - 1]);
|
||||
// If the layer removed was selected then we select the base layer
|
||||
this.selectedLayer = 'base';
|
||||
}
|
||||
|
||||
ALEvent.HIPS_LAYER_REMOVED.dispatchedTo(this.aladinDiv, { layer });
|
||||
@@ -1811,10 +1782,10 @@ export let View = (function () {
|
||||
|
||||
// Set the grid label format
|
||||
if (this.cooFrame.label == "J2000d") {
|
||||
this.setGridOptions({fmt: "HMS"});
|
||||
this.setGridConfig({fmt: "HMS"});
|
||||
}
|
||||
else {
|
||||
this.setGridOptions({fmt: "DMS"});
|
||||
this.setGridConfig({fmt: "DMS"});
|
||||
}
|
||||
|
||||
// Get the new view center position (given in icrs)
|
||||
@@ -1869,12 +1840,11 @@ export let View = (function () {
|
||||
ra = parseFloat(ra);
|
||||
dec = parseFloat(dec);
|
||||
|
||||
if (!Number.isFinite(ra) || !Number.isFinite(dec)) {
|
||||
if (isNaN(ra) || isNaN(dec)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.viewCenter.lon = ra;
|
||||
this.viewCenter.lat = dec;
|
||||
this.viewCenter.lat = dec;
|
||||
//this.updateLocation({lon: this.viewCenter.lon, lat: this.viewCenter.lat});
|
||||
|
||||
// Put a javascript code here to do some animation
|
||||
@@ -1918,13 +1888,7 @@ export let View = (function () {
|
||||
this.catalogs = [];
|
||||
this.overlays = [];
|
||||
this.mocs = [];
|
||||
|
||||
this.allOverlayLayers.forEach((overlay) => {
|
||||
ALEvent.GRAPHIC_OVERLAY_LAYER_REMOVED.dispatchedTo(this.aladinDiv, { layer: overlay });
|
||||
})
|
||||
this.allOverlayLayers = [];
|
||||
|
||||
this.mustClearCatalog = true;
|
||||
this.requestRedraw();
|
||||
};
|
||||
|
||||
@@ -1934,10 +1898,7 @@ export let View = (function () {
|
||||
|
||||
if (layer.type == 'catalog' || layer.type == 'progressivecat') {
|
||||
indexToDelete = this.catalogs.indexOf(layer);
|
||||
|
||||
this.catalogs.splice(indexToDelete, 1);
|
||||
|
||||
this.unselectObjects();
|
||||
}
|
||||
else if (layer.type == 'moc') {
|
||||
indexToDelete = this.mocs.indexOf(layer);
|
||||
@@ -1951,7 +1912,7 @@ export let View = (function () {
|
||||
this.overlays.splice(indexToDelete, 1);
|
||||
}
|
||||
|
||||
ALEvent.GRAPHIC_OVERLAY_LAYER_REMOVED.dispatchedTo(this.aladinDiv, { layer });
|
||||
ALEvent.GRAPHIC_OVERLAY_LAYER_REMOVED.dispatchedTo(this.aladinDiv, { layer: layer });
|
||||
|
||||
this.mustClearCatalog = true;
|
||||
this.requestRedraw();
|
||||
@@ -2022,19 +1983,17 @@ export let View = (function () {
|
||||
let closest = null;
|
||||
|
||||
footprints.forEach((footprint) => {
|
||||
if (!footprint.source || !footprint.source.tooSmallFootprint) {
|
||||
// Hidden footprints are not considered
|
||||
let lineWidth = footprint.getLineWidth();
|
||||
// Hidden footprints are not considered
|
||||
let lineWidth = footprint.getLineWidth();
|
||||
|
||||
footprint.setLineWidth(10.0);
|
||||
if (footprint.isShowing && footprint.isInStroke(ctx, this, x * window.devicePixelRatio, y * window.devicePixelRatio)) {
|
||||
closest = footprint;
|
||||
}
|
||||
footprint.setLineWidth(lineWidth);
|
||||
footprint.setLineWidth(10.0);
|
||||
if (footprint.isShowing && footprint.isInStroke(ctx, this, x, y)) {
|
||||
closest = footprint;
|
||||
}
|
||||
footprint.setLineWidth(lineWidth);
|
||||
|
||||
if (closest) {
|
||||
return closest;
|
||||
}
|
||||
if (closest) {
|
||||
return closest;
|
||||
}
|
||||
})
|
||||
|
||||
@@ -2066,9 +2025,8 @@ export let View = (function () {
|
||||
if (this.catalogs) {
|
||||
for (var k = 0; k < this.catalogs.length; k++) {
|
||||
let catalog = this.catalogs[k];
|
||||
let footprints = catalog.getFootprints();
|
||||
|
||||
let closest = this.closestFootprints(footprints, ctx, x, y);
|
||||
let closest = this.closestFootprints(catalog.footprints, ctx, x, y);
|
||||
if (closest) {
|
||||
//ctx.lineWidth = pastLineWidth;
|
||||
return [closest];
|
||||
|
||||
170
src/js/Zoom.js
170
src/js/Zoom.js
@@ -1,170 +0,0 @@
|
||||
// Copyright 2013 - UDS/CNRS
|
||||
// The Aladin Lite program is distributed under the terms
|
||||
// of the GNU General Public License version 3.
|
||||
//
|
||||
// This file is part of Aladin Lite.
|
||||
//
|
||||
// Aladin Lite is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 3 of the License.
|
||||
//
|
||||
// Aladin Lite is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// The GNU General Public License is available in COPYING file
|
||||
// along with Aladin Lite.
|
||||
//
|
||||
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Aladin Lite project
|
||||
*
|
||||
* File Tile
|
||||
*
|
||||
* Author: Matthieu Baumann[CDS]
|
||||
*
|
||||
*****************************************************************************/
|
||||
import { Utils } from "./Utils";
|
||||
import { requestAnimFrame } from "./libs/RequestAnimationFrame.js";
|
||||
|
||||
|
||||
export let Zoom = (function() {
|
||||
// constructor
|
||||
function Zoom(view) {
|
||||
this.view = view;
|
||||
};
|
||||
|
||||
Zoom.LEVELS = [
|
||||
360, 330, 300, 275, 250, 225, 200, 190,
|
||||
180, 170, 160, 150, 140, 130, 120, 110, 100,
|
||||
95, 90, 85, 80, 75, 70, 65, 60, 55, 50, 45, 40, 35, 30, 25, 20, 18, 16, 14, 12, 10,
|
||||
9, 8, 7, 6, 5, 4, 3, 2, 1.75, 1.5, 1.25, 1,
|
||||
55/60, 50/60, 45/60, 40/60, 35/60, 30/60, 25/60, 20/60, 15/60, 10/60,
|
||||
9/60, 8/60, 7/60, 6/60, 5/60, 4/60, 3/60, 2/60, 1/60,
|
||||
50/3600, 40/3600, 30/3600, 20/3600, 10/3600,
|
||||
9/3600, 8/3600, 7/3600, 6/3600, 5/3600, 4/3600, 3/3600, 2/3600, 1/3600,
|
||||
9/36000, 8/36000, 7/36000, 6/36000, 5/36000, 4/36000, 3/36000, 2/36000, 1/36000
|
||||
];
|
||||
Zoom.MAX_IDX_DELTA_PER_TROTTLE = 6;
|
||||
|
||||
Zoom.determineNextFov = function(view, amount) {
|
||||
const currIdx = Utils.binarySearch(Zoom.LEVELS, view.fov);
|
||||
|
||||
let deltaIdx = amount;
|
||||
let nextIdx = currIdx + deltaIdx;
|
||||
|
||||
// clamp to the array indices
|
||||
if (nextIdx < 0) {
|
||||
nextIdx = 0
|
||||
}
|
||||
|
||||
if (nextIdx >= Zoom.LEVELS.length) {
|
||||
nextIdx = Zoom.LEVELS.length - 1
|
||||
}
|
||||
|
||||
let nextFov = Zoom.LEVELS[nextIdx];
|
||||
|
||||
if (view.minFoV) {
|
||||
nextFov = Math.max(nextFov, view.minFoV)
|
||||
}
|
||||
|
||||
if (view.maxFoV) {
|
||||
nextFov = Math.min(nextFov, view.maxFoV)
|
||||
}
|
||||
|
||||
return nextFov;
|
||||
}
|
||||
|
||||
Zoom.prototype.apply = function(options) {
|
||||
let startZoom = options['start'] || this.view.fov;
|
||||
let finalZoom = options['stop'] || undefined;
|
||||
let interpolationDuration = options['duration'] || 1000; // default to 1seconds
|
||||
if (!finalZoom)
|
||||
return;
|
||||
|
||||
this.finalZoom = finalZoom;
|
||||
|
||||
if (!this.isZooming) {
|
||||
this.isZooming = true;
|
||||
|
||||
this.startTime = performance.now();
|
||||
|
||||
this.x1 = 0
|
||||
this.x2 = 1;
|
||||
this.y1 = startZoom;
|
||||
this.y2 = finalZoom;
|
||||
this.m1 = finalZoom - startZoom;
|
||||
this.m2 = 0;
|
||||
|
||||
this.x = this.x1;
|
||||
} else {
|
||||
// find the startTime
|
||||
this.x = (performance.now() - this.startTime) / interpolationDuration;
|
||||
|
||||
let m1 = Zoom.hermiteCubic.fPrime(this.x, this.x1, this.x2, this.y1, this.y2, this.m1, this.m2)
|
||||
let y1 = Zoom.hermiteCubic.f(this.x, this.x1, this.x2, this.y1, this.y2, this.m1, this.m2);
|
||||
this.y1 = y1;
|
||||
this.x1 = this.x;
|
||||
this.x2 = this.x1 + 1;
|
||||
this.y2 = finalZoom;
|
||||
this.m1 = m1;
|
||||
this.m2 = 0;
|
||||
}
|
||||
|
||||
// Initialize current zoom to the current zoom level
|
||||
let interpolatedZoom;
|
||||
let self = this;
|
||||
// Recursive function to perform interpolation for each frame
|
||||
function interpolateFrame() {
|
||||
//console.log('zooming')
|
||||
//fps = 1000 / self.dt;
|
||||
//totalFrames = interpolationDuration * fps; // Total number of frames
|
||||
self.x = ( performance.now() - self.startTime ) / interpolationDuration;
|
||||
// Calculate step size for each frame
|
||||
//stepSize = (desiredZoom - currentZoom) / totalFrames;
|
||||
interpolatedZoom = Zoom.hermiteCubic.f(self.x, self.x1, self.x2, self.y1, self.y2, self.m1, self.m2);
|
||||
// Clamp the interpolation in case it is < 0 for a time
|
||||
interpolatedZoom = Math.max(Zoom.MIN, interpolatedZoom);
|
||||
|
||||
// Apply zoom level to map or perform any necessary rendering
|
||||
self.view.setZoom(interpolatedZoom);
|
||||
|
||||
self.fov = interpolatedZoom;
|
||||
|
||||
// Check if interpolation is complete
|
||||
if (self.x >= self.x2 || Math.abs(interpolatedZoom - self.finalZoom) < 1e-4) {
|
||||
self.view.setZoom(self.finalZoom);
|
||||
|
||||
self.isZooming = false;
|
||||
} else {
|
||||
// Request the next frame
|
||||
requestAnimFrame(interpolateFrame);
|
||||
}
|
||||
}
|
||||
|
||||
// Start interpolation by requesting the first frame
|
||||
requestAnimFrame(interpolateFrame);
|
||||
}
|
||||
|
||||
Zoom.MAX = Zoom.LEVELS[0];
|
||||
Zoom.MIN = Zoom.LEVELS[Zoom.LEVELS.length - 1];
|
||||
|
||||
Zoom.hermiteCubic = {
|
||||
f: function(x, x1, x2, y1, y2, m1, m2) {
|
||||
let t = (x - x1) / (x2 - x1)
|
||||
let t2 = t*t;
|
||||
let t3 = t2*t;
|
||||
return (1 - 3*t2 + 2*t3) * y1 + (t - 2*t2 + t3) * m1 + (3*t2 - 2*t3) * y2 + (-t2 + t3) * m2
|
||||
},
|
||||
fPrime: function(x, x1, x2, y1, y2, m1, m2) {
|
||||
let t = (x - x1) / (x2 - x1)
|
||||
let t2 = t*t;
|
||||
return (1 / (x2 - x1))*((-6*t+6*t2)*y1 + (1 - 4*t + 3*t2)*m1 + (6*t - 6*t2)*y2 + m2*(3*t2 - 2*t))
|
||||
}
|
||||
}
|
||||
|
||||
return Zoom;
|
||||
})();
|
||||
@@ -53,8 +53,6 @@ export class ALEvent {
|
||||
static HIPS_LAYER_RENAMED = new ALEvent("AL:HiPSLayer.renamed");
|
||||
static HIPS_LAYER_SWAP = new ALEvent("AL:HiPSLayer.swap");
|
||||
|
||||
static HIPS_CACHE_UPDATED = new ALEvent("AL:HiPSCache.updated");
|
||||
|
||||
static HIPS_LAYER_CHANGED = new ALEvent("AL:HiPSLayer.changed");
|
||||
|
||||
static GRAPHIC_OVERLAY_LAYER_ADDED = new ALEvent("AL:GraphicOverlayLayer.added");
|
||||
|
||||
@@ -131,7 +131,6 @@ import { CtxMenuActionButtonOpener } from "../Button/CtxMenuOpener.js";
|
||||
}, aladin)
|
||||
|
||||
super({
|
||||
close: false,
|
||||
content: Layout.horizontal({
|
||||
layout: [inputText, loadBtn]
|
||||
}),
|
||||
@@ -239,10 +238,10 @@ import { CtxMenuActionButtonOpener } from "../Button/CtxMenuOpener.js";
|
||||
this.loadBtn.update({disable: true}, aladin)
|
||||
} else {
|
||||
let self = this;
|
||||
let ctxMenu = [];
|
||||
let layout = [];
|
||||
|
||||
if (item && item.cs_service_url) {
|
||||
ctxMenu.push({
|
||||
layout.push({
|
||||
label: 'Cone search',
|
||||
disable: !item.cs_service_url,
|
||||
action(o) {
|
||||
@@ -264,8 +263,6 @@ import { CtxMenuActionButtonOpener } from "../Button/CtxMenuOpener.js";
|
||||
})
|
||||
|
||||
self._hide();
|
||||
|
||||
self.callback && self.callback();
|
||||
},
|
||||
position: {
|
||||
anchor: 'center center',
|
||||
@@ -273,12 +270,13 @@ import { CtxMenuActionButtonOpener } from "../Button/CtxMenuOpener.js";
|
||||
})
|
||||
self.box._show();
|
||||
self.loadBtn.hideMenu()
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (item && item.hips_service_url) {
|
||||
ctxMenu.push({
|
||||
layout.push({
|
||||
label: 'HiPS catalogue',
|
||||
disable: !item.hips_service_url,
|
||||
action(o) {
|
||||
@@ -288,22 +286,15 @@ import { CtxMenuActionButtonOpener } from "../Button/CtxMenuOpener.js";
|
||||
})
|
||||
|
||||
self._hide();
|
||||
|
||||
self.callback && self.callback();
|
||||
}
|
||||
})
|
||||
}
|
||||
this.loadBtn.update({ctxMenu, disable: false}, aladin)
|
||||
this.loadBtn.update({ctxMenu: layout, disable: false}, aladin)
|
||||
}
|
||||
|
||||
this.loadBtn.hideMenu()
|
||||
}
|
||||
|
||||
attach(options) {
|
||||
this.callback = options.callback;
|
||||
super.update(options)
|
||||
}
|
||||
|
||||
_hide() {
|
||||
if (this.box) {
|
||||
this.box.remove();
|
||||
|
||||
@@ -1,327 +0,0 @@
|
||||
// Copyright 2013 - UDS/CNRS
|
||||
// The Aladin Lite program is distributed under the terms
|
||||
// of the GNU General Public License version 3.
|
||||
//
|
||||
// This file is part of Aladin Lite.
|
||||
//
|
||||
// Aladin Lite is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 3 of the License.
|
||||
//
|
||||
// Aladin Lite is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// The GNU General Public License is available in COPYING file
|
||||
// along with Aladin Lite.
|
||||
//
|
||||
|
||||
import { MocServer } from "../../MocServer.js";
|
||||
|
||||
import { Box } from "../Widgets/Box.js";
|
||||
import { Dropdown } from "../Input/Dropdown.js";
|
||||
import filterOnUrl from "../../../../assets/icons/filter-on.svg";
|
||||
import filterOffUrl from "../../../../assets/icons/filter-off.svg";
|
||||
import { Input } from "../Widgets/Input.js";
|
||||
import { TogglerActionButton } from "../Button/Toggler.js";
|
||||
import { Layout } from "../Layout.js";
|
||||
import { HiPSFilterBox } from "./HiPSFilterBox.js";
|
||||
import A from "../../A.js";
|
||||
import { Utils } from "../../Utils.ts";
|
||||
import { ActionButton } from "../Widgets/ActionButton.js";
|
||||
import infoIconUrl from "../../../../assets/icons/info.svg"
|
||||
|
||||
/******************************************************************************
|
||||
* Aladin Lite project
|
||||
*
|
||||
* File gui/HiPSBrowserBox.js
|
||||
*
|
||||
*
|
||||
* Author: Matthieu Baumann[CDS]
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
export class HiPSBrowserBox extends Box {
|
||||
static HiPSList = {};
|
||||
|
||||
constructor(aladin, options) {
|
||||
let self;
|
||||
|
||||
MocServer.getAllHiPSes().then((HiPSes) => {
|
||||
// Fill the HiPSList from the MOCServer
|
||||
HiPSes.forEach((h) => {
|
||||
HiPSBrowserBox.HiPSList[h.obs_title] = h;
|
||||
});
|
||||
|
||||
// Initialize the autocompletion without any filtering
|
||||
self._filterHiPSList({})
|
||||
});
|
||||
|
||||
const _parseHiPS = (e) => {
|
||||
const value = e.target.value;
|
||||
|
||||
let image;
|
||||
// A user can put an url
|
||||
try {
|
||||
image = new URL(value).href;
|
||||
} catch (e) {
|
||||
// Or he can select a HiPS from the list given
|
||||
const hips = HiPSBrowserBox.HiPSList[value];
|
||||
|
||||
if (hips) {
|
||||
image = hips.ID || hips.hips_service_url;
|
||||
} else {
|
||||
// Finally if not found, interpret the input text value as the HiPS (e.g. ID)
|
||||
image = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (image) {
|
||||
self._addHiPS(image)
|
||||
self.searchDropdown.update({title: value});
|
||||
}
|
||||
};
|
||||
|
||||
let searchDropdown = new Dropdown(aladin, {
|
||||
name: "HiPS browser",
|
||||
placeholder: "Browse a HiPS by an URL, ID or keywords",
|
||||
tooltip: {
|
||||
global: true,
|
||||
aladin,
|
||||
content: 'HiPS url, ID or keyword accepted',
|
||||
},
|
||||
actions: {
|
||||
focus(e) {
|
||||
searchDropdown.removeClass('aladin-valid')
|
||||
searchDropdown.removeClass('aladin-not-valid')
|
||||
},
|
||||
keydown(e) {
|
||||
e.stopPropagation();
|
||||
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault()
|
||||
_parseHiPS(e)
|
||||
}
|
||||
},
|
||||
input(e) {
|
||||
self.infoCurrentHiPSBtn.update({
|
||||
disable: true,
|
||||
})
|
||||
|
||||
searchDropdown.removeClass('aladin-valid')
|
||||
searchDropdown.removeClass('aladin-not-valid')
|
||||
},
|
||||
change(e) {
|
||||
_parseHiPS(e)
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
let filterEnabler = Input.checkbox({
|
||||
name: "filter-enabler",
|
||||
checked: false,
|
||||
tooltip: {
|
||||
content: "Filter off",
|
||||
position: {direction: 'left'},
|
||||
},
|
||||
click(e) {
|
||||
let on = e.target.checked;
|
||||
self.filterBox.enable(on);
|
||||
|
||||
if (!on) {
|
||||
// if the filter has been disabled we also need to update
|
||||
// the autocompletion list of the search dropdown
|
||||
// We give no filter params
|
||||
self._filterHiPSList({});
|
||||
}
|
||||
|
||||
filterBtn.update({
|
||||
icon: {
|
||||
url: on ? filterOnUrl : filterOffUrl,
|
||||
monochrome: true,
|
||||
},
|
||||
});
|
||||
|
||||
filterEnabler.update({
|
||||
tooltip: {
|
||||
content: on
|
||||
? "Filter on"
|
||||
: "Filter off",
|
||||
position: {direction: 'left'},
|
||||
},
|
||||
checked: on,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
let infoCurrentHiPSBtn = new ActionButton({
|
||||
disable: true,
|
||||
icon: {
|
||||
size: 'medium',
|
||||
monochrome: true,
|
||||
url: infoIconUrl,
|
||||
},
|
||||
tooltip: {
|
||||
global: true,
|
||||
aladin,
|
||||
content: "More about that survey?"
|
||||
}
|
||||
});
|
||||
|
||||
let filterBtn = new TogglerActionButton({
|
||||
icon: {
|
||||
url: filterOffUrl,
|
||||
monochrome: true,
|
||||
},
|
||||
size: "small",
|
||||
tooltip: {
|
||||
content: "Want to filter HiPS surveys by criteria ?",
|
||||
position: { direction: "top" },
|
||||
},
|
||||
toggled: false,
|
||||
actionOn: (e) => {
|
||||
self.filterBox._show({
|
||||
position: {
|
||||
nextTo: filterBtn,
|
||||
direction: "right",
|
||||
aladin,
|
||||
},
|
||||
});
|
||||
},
|
||||
actionOff: (e) => {
|
||||
self.filterBox._hide();
|
||||
},
|
||||
});
|
||||
|
||||
super(
|
||||
{
|
||||
close: true,
|
||||
header: {
|
||||
title: "HiPS browser",
|
||||
},
|
||||
onDragged: () => {
|
||||
if (self.filterBtn.toggled) {
|
||||
self.filterBtn.toggle();
|
||||
}
|
||||
},
|
||||
classList: ['aladin-HiPS-browser-box'],
|
||||
content: Layout.vertical([
|
||||
Layout.horizontal(["Search:", searchDropdown, infoCurrentHiPSBtn]),
|
||||
Layout.horizontal(["Filter:", Layout.horizontal([filterEnabler, filterBtn])]),
|
||||
]),
|
||||
...options,
|
||||
},
|
||||
aladin.aladinDiv
|
||||
);
|
||||
|
||||
this.filterBox = new HiPSFilterBox(aladin, {
|
||||
callback: (params) => {
|
||||
self._filterHiPSList(params);
|
||||
},
|
||||
})
|
||||
this.filterBox._hide();
|
||||
|
||||
this.searchDropdown = searchDropdown;
|
||||
this.filterBtn = filterBtn;
|
||||
this.aladin = aladin;
|
||||
|
||||
this.infoCurrentHiPSBtn = infoCurrentHiPSBtn;
|
||||
|
||||
self = this;
|
||||
|
||||
this.filterCallback = (HiPS, params) => {
|
||||
if (!HiPS.obs_regime || (
|
||||
params.regime &&
|
||||
HiPS.obs_regime &&
|
||||
params.regime.toLowerCase() !==
|
||||
HiPS.obs_regime.toLowerCase()
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Array.isArray(params.spatial) && HiPS.ID && !(params.spatial.includes(HiPS.ID))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!HiPS.hips_tile_width || !HiPS.hips_order)
|
||||
return false;
|
||||
|
||||
if (params.resolution) {
|
||||
let pixelHEALPixOrder = Math.log2(HiPS.hips_tile_width) + HiPS.hips_order;
|
||||
let resPixel = Math.sqrt(Math.PI / (3*Math.pow(4, pixelHEALPixOrder)));
|
||||
|
||||
if (resPixel > params.resolution)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
_addHiPS(id) {
|
||||
let self = this;
|
||||
let hips = A.imageHiPS(id, {
|
||||
successCallback: (hips) => {
|
||||
self.searchDropdown.removeClass('aladin-not-valid');
|
||||
self.searchDropdown.addClass('aladin-valid');
|
||||
|
||||
|
||||
self.infoCurrentHiPSBtn.update({
|
||||
disable: false,
|
||||
action(e) {
|
||||
window.open(hips.url);
|
||||
}
|
||||
})
|
||||
},
|
||||
errorCallback: (e) => {
|
||||
self.searchDropdown.removeClass('aladin-valid');
|
||||
self.searchDropdown.addClass('aladin-not-valid');
|
||||
}
|
||||
});
|
||||
this.aladin.setOverlayImageLayer(hips, self.layer);
|
||||
}
|
||||
|
||||
// This method is executed only if the filter is enabled
|
||||
_filterHiPSList(params) {
|
||||
console.log("update dropdown")
|
||||
let self = this;
|
||||
let HiPSIDs = [];
|
||||
|
||||
for (var key in HiPSBrowserBox.HiPSList) {
|
||||
let HiPS = HiPSBrowserBox.HiPSList[key];
|
||||
|
||||
// apply filtering
|
||||
if (
|
||||
self.filterCallback &&
|
||||
self.filterCallback(HiPS, params)
|
||||
) {
|
||||
// search with the name or id
|
||||
HiPSIDs.push(HiPS.obs_title);
|
||||
}
|
||||
}
|
||||
|
||||
self.searchDropdown.update({ options: HiPSIDs });
|
||||
}
|
||||
|
||||
_hide() {
|
||||
if (this.filterBox)
|
||||
this.filterBox.signalBrowserStatus(true)
|
||||
|
||||
if (this.filterBtn && this.filterBtn.toggled) {
|
||||
this.filterBtn.toggle();
|
||||
}
|
||||
|
||||
super._hide()
|
||||
}
|
||||
|
||||
_show(options) {
|
||||
// Regenerate a new layer name
|
||||
this.layer = Utils.uuidv4()
|
||||
|
||||
if (this.filterBox)
|
||||
this.filterBox.signalBrowserStatus(false)
|
||||
|
||||
super._show(options)
|
||||
}
|
||||
}
|
||||
@@ -1,230 +0,0 @@
|
||||
// Copyright 2013 - UDS/CNRS
|
||||
// The Aladin Lite program is distributed under the terms
|
||||
// of the GNU General Public License version 3.
|
||||
//
|
||||
// This file is part of Aladin Lite.
|
||||
//
|
||||
// Aladin Lite is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 3 of the License.
|
||||
//
|
||||
// Aladin Lite is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// The GNU General Public License is available in COPYING file
|
||||
// along with Aladin Lite.
|
||||
//
|
||||
|
||||
import { Box } from "../Widgets/Box.js";
|
||||
import { Form } from "../Widgets/Form.js";
|
||||
import { MocServer } from "../../MocServer.js";
|
||||
import { TogglerActionButton } from "../Button/Toggler.js";
|
||||
import { Layout } from "../Layout.js";
|
||||
import { Angle } from "../../libs/astro/angle.js";
|
||||
import { ALEvent } from "../../events/ALEvent.js";
|
||||
import { Utils } from "../../Utils.ts";
|
||||
import { AladinUtils } from "../../AladinUtils.js";
|
||||
import { Input } from "../Widgets/Input.js";
|
||||
/******************************************************************************
|
||||
* Aladin Lite project
|
||||
*
|
||||
* File gui/HiPSBrowserBox.js
|
||||
*
|
||||
*
|
||||
* Author: Matthieu Baumann[CDS]
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
export class HiPSFilterBox extends Box {
|
||||
constructor(aladin, options) {
|
||||
let self;
|
||||
|
||||
let regimeBtn = new TogglerActionButton({
|
||||
content: 'Regime',
|
||||
tooltip: {content: 'Observation regime', position: {direction: 'bottom'}},
|
||||
toggled: true,
|
||||
actionOn: () => {
|
||||
self._triggerFilteringCallback();
|
||||
},
|
||||
actionOff: () => {
|
||||
self._triggerFilteringCallback();
|
||||
}
|
||||
});
|
||||
let spatialBtn = new TogglerActionButton({
|
||||
content: 'Inside view',
|
||||
tooltip: {content: 'Check for HiPS having observation in the view!', position: {direction: 'bottom'}},
|
||||
toggled: false,
|
||||
actionOn: () => {
|
||||
self._requestMOCServer();
|
||||
},
|
||||
actionOff: () => {
|
||||
self._triggerFilteringCallback();
|
||||
}
|
||||
});
|
||||
let resolutionBtn = new TogglerActionButton({
|
||||
content: 'Pixel res',
|
||||
tooltip: {content: 'Check for HiPS with a specific pixel resolution.', position: {direction: 'bottom'}},
|
||||
toggled: false,
|
||||
actionOn: () => {
|
||||
self._triggerFilteringCallback();
|
||||
},
|
||||
actionOff: () => {
|
||||
self._triggerFilteringCallback();
|
||||
}
|
||||
});
|
||||
|
||||
let logSlider = new Input({
|
||||
label: "Max res [°/px]:",
|
||||
name: "res",
|
||||
value: 0.1,
|
||||
type: 'range',
|
||||
cssStyle: {
|
||||
width: '100%'
|
||||
},
|
||||
tooltip: {content: AladinUtils.degreesToString(0.1), position: {direction: 'bottom'}},
|
||||
ticks: [0.1 / 3600, 1 / 3600, 1 / 60, 0.1],
|
||||
stretch: "log",
|
||||
min: 0.1 / 3600,
|
||||
max: 0.1,
|
||||
reversed: true,
|
||||
change: (e, slider, deg) => {
|
||||
slider.update({value: e.target.value, tooltip: {content: AladinUtils.degreesToString(deg), position:{direction:'bottom'}}});
|
||||
|
||||
let resolution = new Angle(deg);
|
||||
self.params["resolution"] = resolution.degrees();
|
||||
|
||||
self._triggerFilteringCallback();
|
||||
},
|
||||
});
|
||||
super(
|
||||
{
|
||||
classList: ['aladin-HiPS-filter-box'],
|
||||
close: false,
|
||||
content: Layout.vertical([
|
||||
'<b>Filter by:</b>',
|
||||
Layout.horizontal([regimeBtn, spatialBtn, resolutionBtn]),
|
||||
'<b>Details:</b>',
|
||||
new Form({
|
||||
subInputs: [
|
||||
{
|
||||
type: "group",
|
||||
subInputs: [
|
||||
{
|
||||
label: "Regime:",
|
||||
name: "regime",
|
||||
value: "Optical",
|
||||
type: 'select',
|
||||
options: [
|
||||
"Radio",
|
||||
"Infrared",
|
||||
"Millimeter",
|
||||
"Optical",
|
||||
"UV",
|
||||
"EUV",
|
||||
"X-ray",
|
||||
"Gamma-ray",
|
||||
],
|
||||
change: (e) => {
|
||||
let regime = e.target.value;
|
||||
self.params["regime"] = regime;
|
||||
|
||||
//regimeBtn.update({content: regime});
|
||||
|
||||
self._triggerFilteringCallback();
|
||||
},
|
||||
tooltip: {
|
||||
content: "Observation regime",
|
||||
position: { direction: "right" },
|
||||
},
|
||||
},
|
||||
logSlider
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
])
|
||||
},
|
||||
aladin.aladinDiv
|
||||
);
|
||||
|
||||
self = this;
|
||||
|
||||
this.browserClosed = false;
|
||||
|
||||
this.callback = options.callback;
|
||||
|
||||
this.regimeBtn = regimeBtn;
|
||||
this.spatialBtn = spatialBtn;
|
||||
this.resolutionBtn = resolutionBtn;
|
||||
|
||||
this.params = {
|
||||
regime: "Optical",
|
||||
spatial: true,
|
||||
resolution: 1, // 1°/pixel
|
||||
};
|
||||
this.on = false;
|
||||
this.aladin = aladin;
|
||||
this._addListeners();
|
||||
}
|
||||
|
||||
_addListeners() {
|
||||
const requestMOCServerDebounced = Utils.debounce(() => {
|
||||
this._requestMOCServer()
|
||||
}, 500);
|
||||
|
||||
ALEvent.POSITION_CHANGED.listenedBy(this.aladin.aladinDiv, requestMOCServerDebounced);
|
||||
ALEvent.ZOOM_CHANGED.listenedBy(this.aladin.aladinDiv, requestMOCServerDebounced);
|
||||
}
|
||||
|
||||
_requestMOCServer() {
|
||||
if (!this.spatialBtn.toggled || !this.on || this.browserClosed) {
|
||||
return;
|
||||
}
|
||||
|
||||
let self = this;
|
||||
MocServer.getAllHiPSesInsideView(this.aladin)
|
||||
.then((HiPSes) => {
|
||||
let HiPSIDs = HiPSes.map((x) => x.ID);
|
||||
self.params["spatial"] = HiPSIDs;
|
||||
|
||||
self._triggerFilteringCallback();
|
||||
})
|
||||
}
|
||||
|
||||
_triggerFilteringCallback() {
|
||||
let filterParams = {};
|
||||
|
||||
if (this.regimeBtn.toggled) {
|
||||
filterParams['regime'] = this.params['regime']
|
||||
}
|
||||
|
||||
if (this.spatialBtn.toggled) {
|
||||
filterParams['spatial'] = this.params['spatial']
|
||||
}
|
||||
|
||||
if (this.resolutionBtn.toggled) {
|
||||
filterParams['resolution'] = this.params['resolution']
|
||||
}
|
||||
|
||||
if (this.on && this.callback) {
|
||||
this.callback(filterParams);
|
||||
}
|
||||
}
|
||||
|
||||
signalBrowserStatus(closed) {
|
||||
this.browserClosed = closed;
|
||||
|
||||
// open
|
||||
if (!closed) {
|
||||
this._requestMOCServer()
|
||||
}
|
||||
}
|
||||
|
||||
enable(enable) {
|
||||
this.on = enable;
|
||||
|
||||
this._triggerFilteringCallback();
|
||||
}
|
||||
}
|
||||
@@ -79,7 +79,6 @@ import { Input } from "../Widgets/Input.js";
|
||||
|
||||
super(
|
||||
{
|
||||
close: false,
|
||||
content: Layout.horizontal({
|
||||
layout: [
|
||||
inputText,
|
||||
|
||||
@@ -75,7 +75,6 @@ export class ServiceQueryBox extends Box {
|
||||
Utils.loadFromUrls([url, Utils.handleCORSNotSameOrigin(url)], {timeout: 30000, dataType: 'blob'})
|
||||
.then((blob) => {
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
try {
|
||||
let image = self.aladin.createImageFITS(url, name);
|
||||
self.aladin.setOverlayImageLayer(image, Utils.uuidv4())
|
||||
@@ -150,8 +149,8 @@ export class ServiceQueryBox extends Box {
|
||||
|
||||
let fov = new Angle(radius, 1).degrees();
|
||||
//selectorBtn.update({tooltip: {content: 'center: ' + ra.toFixed(2) + ', ' + dec.toFixed(2) + '<br\>radius: ' + radius.toFixed(2), position: {direction: 'left'}}})
|
||||
self.form.set('ra', lon)
|
||||
self.form.set('dec', lat)
|
||||
self.form.set('ra', +lon)
|
||||
self.form.set('dec', +lat)
|
||||
self.form.set('rad', fov)
|
||||
} catch (e) {
|
||||
alert(e, 'Cone search out of projection')
|
||||
|
||||
@@ -35,7 +35,6 @@ export class ShortLivedBox extends Box {
|
||||
constructor(aladin) {
|
||||
super(
|
||||
{
|
||||
close: false,
|
||||
cssStyle: {
|
||||
color: 'white',
|
||||
backgroundColor: 'black',
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -38,7 +38,7 @@ import { Icon } from "../Widgets/Icon";
|
||||
|
||||
export class StatusBarBox extends Box {
|
||||
constructor(aladin, options) {
|
||||
super({...options, close: false}, aladin.aladinDiv)
|
||||
super(options, aladin.aladinDiv)
|
||||
|
||||
this.addClass("aladin-status-bar");
|
||||
|
||||
|
||||
@@ -40,14 +40,15 @@ import { ColorCfg } from "../../ColorCfg.js";
|
||||
import { Layout } from "../Layout.js";
|
||||
import { Input } from "../Widgets/Input.js";
|
||||
|
||||
export class HiPSSettingsBox extends Box {
|
||||
export class LayerEditBox extends Box {
|
||||
// Constructor
|
||||
constructor(aladin, options) {
|
||||
super({
|
||||
super(
|
||||
{
|
||||
cssStyle: {
|
||||
backgroundColor: '#333333'
|
||||
padding: '4px',
|
||||
backgroundColor: 'black',
|
||||
},
|
||||
close: false,
|
||||
...options
|
||||
},
|
||||
aladin.aladinDiv
|
||||
@@ -161,19 +162,21 @@ import { ColorCfg } from "../../ColorCfg.js";
|
||||
|
||||
let layerOpacity = layer.getOpacity()
|
||||
|
||||
self.opacitySettingsContent = Input.slider({
|
||||
tooltip: {content: layerOpacity, position: {direction: 'bottom'}},
|
||||
name: 'opacitySlider',
|
||||
type: 'range',
|
||||
min: 0.0,
|
||||
max: 1.0,
|
||||
value: layerOpacity,
|
||||
change(e, slider) {
|
||||
const opacity = +e.target.value;
|
||||
layer.setOpacity(opacity)
|
||||
slider.update({value: opacity, tooltip: {content: opacity.toFixed(2), position: {direction: 'bottom'}}})
|
||||
}
|
||||
})
|
||||
self.opacitySettingsContent = Layout.horizontal([
|
||||
Input.slider({
|
||||
tooltip: {content: layerOpacity, position: {direction: 'bottom'}},
|
||||
name: 'opacitySlider',
|
||||
type: 'range',
|
||||
min: 0.0,
|
||||
max: 1.0,
|
||||
value: layerOpacity,
|
||||
change(e, slider) {
|
||||
const opacity = +e.target.value;
|
||||
layer.setOpacity(opacity)
|
||||
slider.update({value: opacity, tooltip: {content: opacity.toFixed(2), position: {direction: 'bottom'}}})
|
||||
}
|
||||
}),
|
||||
]);
|
||||
|
||||
let brightness = layer.getColorCfg().getBrightness()
|
||||
let saturation = layer.getColorCfg().getSaturation()
|
||||
@@ -308,10 +311,10 @@ import { ColorCfg } from "../../ColorCfg.js";
|
||||
|
||||
_addListeners() {
|
||||
ALEvent.HIPS_LAYER_CHANGED.listenedBy(this.aladin.aladinDiv, (e) => {
|
||||
const hips = e.detail.layer;
|
||||
const layerChanged = e.detail.layer;
|
||||
let selectedLayer = this.options.layer;
|
||||
if (selectedLayer && hips.layer === selectedLayer.layer) {
|
||||
let colorCfg = hips.getColorCfg();
|
||||
if (selectedLayer && layerChanged.layer === selectedLayer.layer) {
|
||||
let colorCfg = layerChanged.getColorCfg();
|
||||
|
||||
let cmap = colorCfg.getColormap();
|
||||
let reversed = colorCfg.getReversed();
|
||||
@@ -320,9 +323,7 @@ import { ColorCfg } from "../../ColorCfg.js";
|
||||
let [minCut, maxCut] = colorCfg.getCuts();
|
||||
this.minCutInput.set(+minCut.toFixed(2));
|
||||
this.maxCutInput.set(+maxCut.toFixed(2));
|
||||
this.stretchSelector.update({value: stretch});
|
||||
|
||||
this.opacitySettingsContent.set(hips.getOpacity())
|
||||
this.stretchSelector.update({value: stretch})
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -19,8 +19,7 @@
|
||||
|
||||
import { CtxMenuActionButtonOpener } from "./CtxMenuOpener";
|
||||
import stackOverlayIconUrl from './../../../../assets/icons/stack.svg';
|
||||
import { OverlayStackBox } from "../Box/StackBox";
|
||||
import { TogglerActionButton } from "./Toggler";
|
||||
import { OverlayStack } from "../CtxMenu/OverlayStack";
|
||||
/******************************************************************************
|
||||
* Aladin Lite project
|
||||
*
|
||||
@@ -36,15 +35,14 @@ import { TogglerActionButton } from "./Toggler";
|
||||
* Class representing a Tabs layout
|
||||
* @extends CtxMenuActionButtonOpener
|
||||
*/
|
||||
export class OverlayStackButton extends TogglerActionButton {
|
||||
export class OverlayStackButton extends CtxMenuActionButtonOpener {
|
||||
/**
|
||||
* UI responsible for displaying the viewport infos
|
||||
* @param {Aladin} aladin - The aladin instance.
|
||||
*/
|
||||
constructor(aladin, options) {
|
||||
let self;
|
||||
let stack = new OverlayStackBox(aladin);
|
||||
|
||||
let stack = new OverlayStack(aladin);
|
||||
super({
|
||||
icon: {
|
||||
size: 'medium',
|
||||
@@ -58,18 +56,7 @@ import { TogglerActionButton } from "./Toggler";
|
||||
direction: 'top right'
|
||||
}
|
||||
},
|
||||
toggled: false,
|
||||
actionOn: (e) => {
|
||||
stack._show({
|
||||
position: {
|
||||
nextTo: self,
|
||||
direction: 'right'
|
||||
}
|
||||
})
|
||||
},
|
||||
actionOff: (e) => {
|
||||
stack._hide()
|
||||
},
|
||||
ctxMenu: stack,
|
||||
...options
|
||||
}, aladin);
|
||||
|
||||
|
||||
@@ -43,26 +43,20 @@ export class TogglerActionButton extends ActionButton {
|
||||
...options,
|
||||
toggled,
|
||||
action(o) {
|
||||
self.toggle(o);
|
||||
toggled = !toggled;
|
||||
|
||||
self.update({toggled, tooltip: toggled ? options.tooltipOn : options.tooltipOff})
|
||||
if (toggled && options.actionOn) {
|
||||
options.actionOn(o)
|
||||
}
|
||||
|
||||
if (!toggled && options.actionOff) {
|
||||
options.actionOff(o)
|
||||
}
|
||||
|
||||
options.action && options.action(o)
|
||||
}
|
||||
})
|
||||
this.toggled = toggled;
|
||||
|
||||
self = this;
|
||||
}
|
||||
|
||||
toggle(o) {
|
||||
this.toggled = !this.toggled;
|
||||
|
||||
if (this.toggled && this.options.actionOn) {
|
||||
this.options.actionOn(o)
|
||||
}
|
||||
|
||||
if (!this.toggled && this.options.actionOff) {
|
||||
this.options.actionOff(o)
|
||||
}
|
||||
|
||||
// once the actions has been executed, modify the styling
|
||||
this.update({toggled: this.toggled, tooltip: this.toggled ? this.options.tooltipOn : this.options.tooltipOff})
|
||||
}
|
||||
}
|
||||
883
src/js/gui/CtxMenu/OverlayStack.js
Normal file
883
src/js/gui/CtxMenu/OverlayStack.js
Normal file
@@ -0,0 +1,883 @@
|
||||
// Copyright 2013 - UDS/CNRS
|
||||
// The Aladin Lite program is distributed under the terms
|
||||
// of the GNU General Public License version 3.
|
||||
//
|
||||
// This file is part of Aladin Lite.
|
||||
//
|
||||
// Aladin Lite is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 3 of the License.
|
||||
//
|
||||
// Aladin Lite is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// The GNU General Public License is available in COPYING file
|
||||
// along with Aladin Lite.
|
||||
//
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Aladin Lite project
|
||||
*
|
||||
* File gui/Stack/Menu.js
|
||||
*
|
||||
*
|
||||
* Author: Matthieu Baumann [CDS, matthieu.baumann@astro.unistra.fr]
|
||||
*
|
||||
*****************************************************************************/
|
||||
import { CatalogQueryBox } from "../Box/CatalogQueryBox.js";
|
||||
import { ALEvent } from "../../events/ALEvent.js";
|
||||
import { Layout } from "../Layout.js";
|
||||
import { ContextMenu } from "../Widgets/ContextMenu.js";
|
||||
import { ActionButton } from "../Widgets/ActionButton.js";
|
||||
import A from "../../A.js";
|
||||
import { Utils } from "../../../js/Utils";
|
||||
import { View } from "../../View.js";
|
||||
import { LayerEditBox } from "../Box/SurveyEditBox.js";
|
||||
import { HiPSSelectorBox } from "../Box/HiPSSelectorBox.js";
|
||||
import searchIconUrl from '../../../../assets/icons/search.svg';
|
||||
import showIconUrl from '../../../../assets/icons/show.svg';
|
||||
import hideIconUrl from '../../../../assets/icons/hide.svg';
|
||||
import removeIconUrl from '../../../../assets/icons/remove.svg';
|
||||
import settingsIconUrl from '../../../../assets/icons/settings.svg';
|
||||
import { ImageFITS } from "../../ImageFITS.js";
|
||||
import searchIconImg from '../../../../assets/icons/search.svg';
|
||||
import { TogglerActionButton } from "../Button/Toggler.js";
|
||||
import { Icon } from "../Widgets/Icon.js";
|
||||
import { ImageSurvey } from "../../ImageSurvey.js";
|
||||
|
||||
export class OverlayStack extends ContextMenu {
|
||||
static previewImagesUrl = {
|
||||
'AllWISE color': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_allWISE_color.jpg',
|
||||
'DSS colored': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_DSS2_color.jpg',
|
||||
'DSS2 Red (F+R)': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_DSS2_red.jpg',
|
||||
'Fermi color': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_Fermi_color.jpg',
|
||||
'GALEXGR6_7 NUV': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_GALEXGR6_7_color.jpg',
|
||||
'GLIMPSE360': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_GLIMPSE360.jpg',
|
||||
'Halpha': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_VTSS_Ha.jpg',
|
||||
'IRAC color I1,I2,I4 - (GLIMPSE, SAGE, SAGE-SMC, SINGS)': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_SPITZER_color.jpg',
|
||||
'IRIS colored': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_IRIS_color.jpg',
|
||||
'Mellinger colored': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_Mellinger_color.jpg',
|
||||
'PanSTARRS DR1 color': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_PanSTARRS_DR1_color-z-zg-g.jpg',
|
||||
'2MASS colored': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_2MASS_color.jpg',
|
||||
'AKARI colored': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_AKARI_FIS_Color.jpg',
|
||||
'SWIFT': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_SWIFT_BAT_FLUX.jpg',
|
||||
'VTSS-Ha': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_Finkbeiner.jpg',
|
||||
'XMM PN colored': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_XMM_PN_color.jpg',
|
||||
'SDSS9 colored': 'https://aladin.cds.unistra.fr/AladinLite/survey-previews/P_SDSS9_color.jpg',
|
||||
};
|
||||
static predefinedCats = {
|
||||
simbad: {url: 'https://axel.u-strasbg.fr/HiPSCatService/SIMBAD', options: {id: 'simbad', name: 'SIMBAD', shape: 'circle', sourceSize: 8, color: '#318d80', onClick: 'showTable'}},
|
||||
gaia: {url: 'https://axel.u-strasbg.fr/HiPSCatService/I/355/gaiadr3', options: {id: 'gaia-dr3', name: 'Gaia DR3', shape: 'square', sourceSize: 8, color: '#6baed6', onClick: 'showTable'}},
|
||||
twomass: {url: 'https://axel.u-strasbg.fr/HiPSCatService/II/246/out', options: {id: '2mass', name: '2MASS', shape: 'plus', sourceSize: 8, color: '#dd2233', onClick: 'showTable'}}
|
||||
};
|
||||
// Constructor
|
||||
constructor(aladin) {
|
||||
let self;
|
||||
super(aladin, {hideOnClick: (e) => {
|
||||
// only hide the stack ctx menu but not the windows
|
||||
super._hide();
|
||||
}});
|
||||
self = this;
|
||||
this.aladin = aladin;
|
||||
|
||||
this.mode = 'stack';
|
||||
|
||||
this._addListeners();
|
||||
|
||||
this.mocHiPSUrls = {}
|
||||
}
|
||||
|
||||
_addListeners() {
|
||||
let self = this;
|
||||
|
||||
let updateOverlayList = () => {
|
||||
// If it is shown, update it
|
||||
if (!self.isHidden) {
|
||||
// show will update the content of the stack
|
||||
self.attach();
|
||||
self.show();
|
||||
}
|
||||
};
|
||||
|
||||
ALEvent.GRAPHIC_OVERLAY_LAYER_ADDED.listenedBy(this.aladin.aladinDiv, function (e) {
|
||||
updateOverlayList();
|
||||
});
|
||||
|
||||
ALEvent.GRAPHIC_OVERLAY_LAYER_REMOVED.listenedBy(this.aladin.aladinDiv, function (e) {
|
||||
updateOverlayList();
|
||||
});
|
||||
|
||||
ALEvent.GRAPHIC_OVERLAY_LAYER_CHANGED.listenedBy(this.aladin.aladinDiv, function (e) {
|
||||
updateOverlayList();
|
||||
});
|
||||
|
||||
ALEvent.HIPS_LAYER_ADDED.listenedBy(this.aladin.aladinDiv, function (e) {
|
||||
updateOverlayList();
|
||||
});
|
||||
|
||||
ALEvent.HIPS_LAYER_RENAMED.listenedBy(this.aladin.aladinDiv, function (e) {
|
||||
updateOverlayList();
|
||||
});
|
||||
|
||||
ALEvent.HIPS_LAYER_SWAP.listenedBy(this.aladin.aladinDiv, function (e) {
|
||||
updateOverlayList();
|
||||
});
|
||||
|
||||
ALEvent.HIPS_LAYER_REMOVED.listenedBy(this.aladin.aladinDiv, function (e) {
|
||||
updateOverlayList();
|
||||
});
|
||||
|
||||
updateOverlayList();
|
||||
}
|
||||
|
||||
attach() {
|
||||
let self = this;
|
||||
|
||||
const overlays = Array.from(this.aladin.getOverlays()).reverse().map((overlay) => {
|
||||
return overlay;
|
||||
});
|
||||
const layers = Array.from(self.aladin.getImageOverlays()).reverse().map((name) => {
|
||||
let overlay = self.aladin.getOverlayImageLayer(name);
|
||||
return overlay;
|
||||
});
|
||||
|
||||
|
||||
let layout = [{
|
||||
label: 'Add overlay',
|
||||
subMenu: [
|
||||
{
|
||||
label: 'Catalogue',
|
||||
subMenu: [
|
||||
{
|
||||
label: {
|
||||
icon: {
|
||||
url: 'https://aladin.cds.unistra.fr/AladinLite/logos/SIMBAD.svg',
|
||||
cssStyle: {
|
||||
width: '3rem',
|
||||
height: '3rem',
|
||||
cursor: 'help',
|
||||
},
|
||||
action(o) {
|
||||
window.open('https://simbad.cds.unistra.fr/simbad/')
|
||||
}
|
||||
},
|
||||
content: 'database',
|
||||
tooltip: {content: 'Click to go to the SIMBAD database', position: {direction: 'bottom'}},
|
||||
|
||||
},
|
||||
action(o) {
|
||||
o.stopPropagation();
|
||||
o.preventDefault();
|
||||
|
||||
self._hide();
|
||||
|
||||
const simbadHiPS = A.catalogHiPS(OverlayStack.predefinedCats.simbad.url, OverlayStack.predefinedCats.simbad.options);
|
||||
self.aladin.addCatalog(simbadHiPS);
|
||||
|
||||
self.mode = 'stack';
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'Gaia DR3',
|
||||
action(o) {
|
||||
o.stopPropagation();
|
||||
o.preventDefault();
|
||||
|
||||
self._hide();
|
||||
|
||||
const simbadHiPS = A.catalogHiPS(OverlayStack.predefinedCats.gaia.url, OverlayStack.predefinedCats.gaia.options);
|
||||
self.aladin.addCatalog(simbadHiPS);
|
||||
|
||||
self.mode = 'stack';
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '2MASS',
|
||||
action(o) {
|
||||
o.stopPropagation();
|
||||
o.preventDefault();
|
||||
|
||||
self._hide();
|
||||
|
||||
const simbadHiPS = A.catalogHiPS(OverlayStack.predefinedCats.twomass.url, OverlayStack.predefinedCats.twomass.options);
|
||||
self.aladin.addCatalog(simbadHiPS);
|
||||
|
||||
self.mode = 'stack';
|
||||
}
|
||||
},
|
||||
ContextMenu.fileLoaderItem({
|
||||
label: 'From a VOTable File',
|
||||
accept: '.xml,.vot',
|
||||
action(file) {
|
||||
let url = URL.createObjectURL(file);
|
||||
|
||||
A.catalogFromURL(
|
||||
url,
|
||||
{onClick: 'showTable'},
|
||||
(catalog) => {
|
||||
self.aladin.addCatalog(catalog)
|
||||
},
|
||||
e => alert(e)
|
||||
);
|
||||
}
|
||||
}),
|
||||
{
|
||||
label: {
|
||||
icon: {
|
||||
url: searchIconImg,
|
||||
monochrome: true,
|
||||
tooltip: {content: 'Find a specific catalogue <br /> in our database...', position: { direction: 'top' }},
|
||||
cssStyle: {
|
||||
cursor: 'help',
|
||||
},
|
||||
},
|
||||
content: 'More...'
|
||||
},
|
||||
action(o) {
|
||||
o.stopPropagation();
|
||||
o.preventDefault();
|
||||
|
||||
self._hide();
|
||||
|
||||
self.catBox = new CatalogQueryBox(self.aladin);
|
||||
self.catBox._show({position: self.position});
|
||||
|
||||
self.mode = 'search';
|
||||
}
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
label: {
|
||||
icon: {
|
||||
url: Icon.dataURLFromSVG({svg: Icon.SVG_ICONS.MOC}),
|
||||
size: 'small',
|
||||
tooltip: {content: 'Define a selection coverage', position: {direction: 'bottom'}},
|
||||
monochrome: true,
|
||||
cssStyle: {
|
||||
cursor: 'pointer',
|
||||
},
|
||||
},
|
||||
content: 'MOC'
|
||||
},
|
||||
subMenu: [
|
||||
ContextMenu.fileLoaderItem({
|
||||
label: 'FITS File',
|
||||
accept: '.fits',
|
||||
action(file) {
|
||||
let url = URL.createObjectURL(file);
|
||||
|
||||
let moc = A.MOCFromURL(
|
||||
url,
|
||||
{name: file.name, lineWidth: 3.0},
|
||||
);
|
||||
self.aladin.addMOC(moc)
|
||||
}
|
||||
}),
|
||||
{
|
||||
label: 'From selection',
|
||||
subMenu: [
|
||||
{
|
||||
label: '◌ Circle',
|
||||
disabled: self.aladin.view.mode !== View.PAN ? {
|
||||
reason: 'Exit your current mode<br/>(e.g. disable the SIMBAD pointer mode)'
|
||||
} : false,
|
||||
action(o) {
|
||||
o.preventDefault();
|
||||
o.stopPropagation();
|
||||
|
||||
self._hide();
|
||||
|
||||
self.aladin.select('circle', c => {
|
||||
try {
|
||||
let [ra, dec] = self.aladin.pix2world(c.x, c.y, 'j2000');
|
||||
let radius = self.aladin.angularDist(c.x, c.y, c.x + c.r, c.y);
|
||||
|
||||
// the moc needs a
|
||||
let moc = A.MOCFromCircle(
|
||||
{ra, dec, radius},
|
||||
{name: 'cone', lineWidth: 3.0},
|
||||
);
|
||||
self.aladin.addMOC(moc)
|
||||
} catch {
|
||||
console.error('Circle out of projection. Selection canceled')
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '⬚ Rect',
|
||||
disabled: self.aladin.view.mode !== View.PAN ? {
|
||||
reason: 'Exit your current mode<br/>(e.g. disable the SIMBAD pointer mode)'
|
||||
} : false,
|
||||
action(o) {
|
||||
o.stopPropagation();
|
||||
o.preventDefault();
|
||||
|
||||
self._hide();
|
||||
|
||||
self.aladin.select('rect', r => {
|
||||
try {
|
||||
let [ra1, dec1] = self.aladin.pix2world(r.x, r.y, 'j2000');
|
||||
let [ra2, dec2] = self.aladin.pix2world(r.x + r.w, r.y, 'j2000');
|
||||
let [ra3, dec3] = self.aladin.pix2world(r.x + r.w, r.y + r.h, 'j2000');
|
||||
let [ra4, dec4] = self.aladin.pix2world(r.x, r.y + r.h, 'j2000');
|
||||
|
||||
let moc = A.MOCFromPolygon(
|
||||
{
|
||||
ra: [ra1, ra2, ra3, ra4],
|
||||
dec: [dec1, dec2, dec3, dec4]
|
||||
},
|
||||
{name: 'rect', lineWidth: 3.0},
|
||||
);
|
||||
self.aladin.addMOC(moc)
|
||||
} catch(_) {
|
||||
alert('Selection covers a region out of the projection definition domain.');
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '⛉ Polygon',
|
||||
disabled: self.aladin.view.mode !== View.PAN ? {
|
||||
reason: 'Exit your current mode<br/>(e.g. disable the SIMBAD pointer mode)'
|
||||
} : false,
|
||||
action(o) {
|
||||
o.stopPropagation();
|
||||
o.preventDefault();
|
||||
|
||||
self._hide();
|
||||
|
||||
self.aladin.select('poly', p => {
|
||||
try {
|
||||
let ra = []
|
||||
let dec = []
|
||||
for (const v of p.vertices) {
|
||||
let [lon, lat] = self.aladin.pix2world(v.x, v.y, 'j2000');
|
||||
ra.push(lon)
|
||||
dec.push(lat)
|
||||
}
|
||||
|
||||
let moc = A.MOCFromPolygon(
|
||||
{ra, dec},
|
||||
{name: 'poly', lineWidth: 3.0},
|
||||
);
|
||||
self.aladin.addMOC(moc)
|
||||
|
||||
} catch(_) {
|
||||
alert('Selection covers a region out of the projection definition domain.');
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}];
|
||||
|
||||
for(const overlay of overlays) {
|
||||
const name = overlay.name;
|
||||
let cssStyle = {
|
||||
height: 'fit-content',
|
||||
};
|
||||
let showBtn = new ActionButton({
|
||||
size: 'small',
|
||||
icon: {
|
||||
url: overlay.isShowing ? showIconUrl : hideIconUrl,
|
||||
monochrome: true,
|
||||
},
|
||||
cssStyle: {
|
||||
visibility: Utils.hasTouchScreen() ? 'visible' : 'hidden',
|
||||
},
|
||||
tooltip: {content: overlay.isShowing ? 'Hide' : 'Show', position: {direction: 'bottom'}},
|
||||
action(e, btn) {
|
||||
if (overlay.isShowing) {
|
||||
overlay.hide()
|
||||
btn.update({icon: {monochrome: true, url: hideIconUrl}, tooltip: {content: 'Show'}});
|
||||
} else {
|
||||
overlay.show()
|
||||
btn.update({icon: {monochrome: true, url: showIconUrl}, tooltip: {content: 'Hide'}});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let deleteBtn = new ActionButton({
|
||||
icon: {
|
||||
url: removeIconUrl,
|
||||
monochrome: true,
|
||||
},
|
||||
size: 'small',
|
||||
cssStyle: {
|
||||
visibility: Utils.hasTouchScreen() ? 'visible' : 'hidden',
|
||||
},
|
||||
tooltip: {
|
||||
content: 'Remove',
|
||||
position: {direction: 'bottom'}
|
||||
},
|
||||
action(e) {
|
||||
self.aladin.removeLayer(overlay)
|
||||
}
|
||||
});
|
||||
|
||||
let item = Layout.horizontal({
|
||||
layout: [
|
||||
this._addOverlayIcon(overlay),
|
||||
'<div style="background-color: rgba(0, 0, 0, 0.6); padding: 3px; border-radius: 3px; word-break: break-word;">' + name + '</div>',
|
||||
Layout.horizontal({layout: [showBtn, deleteBtn]})
|
||||
],
|
||||
cssStyle: {
|
||||
textAlign: 'center',
|
||||
}
|
||||
});
|
||||
|
||||
if(!Utils.hasTouchScreen()) {
|
||||
layout.push({
|
||||
label: item,
|
||||
cssStyle,
|
||||
hover(e) {
|
||||
showBtn.el.style.visibility = 'visible'
|
||||
deleteBtn.el.style.visibility = 'visible'
|
||||
},
|
||||
unhover(e) {
|
||||
showBtn.el.style.visibility = 'hidden'
|
||||
deleteBtn.el.style.visibility = 'hidden'
|
||||
},
|
||||
})
|
||||
} else {
|
||||
layout.push({
|
||||
label: item,
|
||||
cssStyle
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
layout.push({
|
||||
label: 'Add survey',
|
||||
subMenu: [
|
||||
{
|
||||
label: {
|
||||
icon: {
|
||||
url: searchIconUrl,
|
||||
monochrome: true,
|
||||
tooltip: {content: 'From our database...', position: { direction: 'right' }},
|
||||
cssStyle: {
|
||||
cursor: 'help',
|
||||
},
|
||||
},
|
||||
content: 'Search for a survey'
|
||||
},
|
||||
action: (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
self._hide();
|
||||
|
||||
self.hipsSelectorBox = new HiPSSelectorBox(self.aladin);
|
||||
// attach a callback
|
||||
self.hipsSelectorBox.attach(
|
||||
(HiPSId) => {
|
||||
let name = Utils.uuidv4()
|
||||
self.aladin.setOverlayImageLayer(HiPSId, name)
|
||||
|
||||
self.show();
|
||||
}
|
||||
);
|
||||
|
||||
self.hipsSelectorBox._show({
|
||||
position: self.position,
|
||||
});
|
||||
|
||||
self.mode = 'hips';
|
||||
}
|
||||
},
|
||||
ContextMenu.fileLoaderItem({
|
||||
label: 'FITS image file',
|
||||
accept: '.fits',
|
||||
action(file) {
|
||||
let url = URL.createObjectURL(file);
|
||||
|
||||
const image = self.aladin.createImageFITS(
|
||||
url,
|
||||
file.name,
|
||||
undefined,
|
||||
(ra, dec, fov, _) => {
|
||||
// Center the view around the new fits object
|
||||
self.aladin.gotoRaDec(ra, dec);
|
||||
self.aladin.setFoV(fov * 1.1);
|
||||
//self.aladin.selectLayer(image.layer);
|
||||
},
|
||||
undefined
|
||||
);
|
||||
|
||||
self.aladin.setOverlayImageLayer(image, Utils.uuidv4())
|
||||
}
|
||||
}),
|
||||
]
|
||||
})
|
||||
|
||||
// survey list
|
||||
let selectedLayer = self.aladin.getSelectedLayer();
|
||||
|
||||
/*if (!layers) {
|
||||
super.attach(layout);
|
||||
return;
|
||||
}*/
|
||||
|
||||
const defaultLayers = Object.entries(ImageSurvey.cache).sort(function (e1, e2) {
|
||||
let a = e1[1]
|
||||
let b = e2[1]
|
||||
|
||||
if (!a.order) {
|
||||
return a.name > b.name ? 1 : -1;
|
||||
}
|
||||
|
||||
return a.maxOrder && a.maxOrder > b.maxOrder ? 1 : -1;
|
||||
});
|
||||
|
||||
for(const layer of layers) {
|
||||
let backgroundUrl = layer.url + '/preview.jpg';
|
||||
let cssStyle = {
|
||||
height: 'fit-content',
|
||||
};
|
||||
|
||||
if (backgroundUrl) {
|
||||
cssStyle = {
|
||||
backgroundSize: '100%',
|
||||
backgroundImage: 'url(' + backgroundUrl + ')',
|
||||
...cssStyle
|
||||
}
|
||||
}
|
||||
|
||||
let showBtn = ActionButton.createSmallSizedIconBtn({
|
||||
icon: {
|
||||
url: layer.getOpacity() === 0.0 ? hideIconUrl : showIconUrl,
|
||||
monochrome: true,
|
||||
},
|
||||
cssStyle: {
|
||||
visibility: Utils.hasTouchScreen() ? 'visible' : 'hidden',
|
||||
},
|
||||
tooltip: {content: layer.getOpacity() === 0.0 ? 'Show' : 'Hide', position: {direction: 'bottom'}},
|
||||
action(e, btn) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
let opacity = layer.getOpacity();
|
||||
if (opacity === 0.0) {
|
||||
layer.setOpacity(1.0);
|
||||
btn.update({icon: {monochrome: true, url: showIconUrl}, tooltip: {content: 'Hide'}});
|
||||
} else {
|
||||
layer.setOpacity(0.0);
|
||||
btn.update({icon: {monochrome: true, url: hideIconUrl}, tooltip: {content: 'Show'}});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let deleteBtn = ActionButton.createSmallSizedIconBtn({
|
||||
icon: {url: removeIconUrl, monochrome: true},
|
||||
cssStyle: {
|
||||
visibility: Utils.hasTouchScreen() ? 'visible' : 'hidden',
|
||||
},
|
||||
disable: layer.layer === 'base',
|
||||
tooltip: {content: 'Remove', position: {direction: 'bottom'}},
|
||||
action(e) {
|
||||
self.aladin.removeImageLayer(layer.layer);
|
||||
}
|
||||
});
|
||||
|
||||
let editBtn = ActionButton.createSmallSizedIconBtn({
|
||||
icon: {url: settingsIconUrl, monochrome: true},
|
||||
cssStyle: {
|
||||
visibility: Utils.hasTouchScreen() ? 'visible' : 'hidden',
|
||||
},
|
||||
tooltip: {content: 'Settings', position: {direction: 'bottom'}},
|
||||
action: (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
self._hide();
|
||||
|
||||
//self.aladin.selectLayer(layer.layer);
|
||||
//self.attach()
|
||||
|
||||
self.editBox = new LayerEditBox(self.aladin);
|
||||
self.editBox.update({layer})
|
||||
self.editBox._show({position: self.position});
|
||||
|
||||
self.mode = 'edit';
|
||||
}
|
||||
});
|
||||
|
||||
let loadMOCBtn = new TogglerActionButton({
|
||||
size: 'small',
|
||||
cssStyle: {
|
||||
visibility: Utils.hasTouchScreen() ? 'visible' : 'hidden',
|
||||
},
|
||||
icon: {url: Icon.dataURLFromSVG({svg: Icon.SVG_ICONS.MOC}), monochrome: true},
|
||||
tooltip: {content: 'Add coverage', position: {direction: 'bottom'}},
|
||||
toggled: (() => {
|
||||
let overlays = self.aladin.getOverlays();
|
||||
let found = overlays.find((o) => o.type === "moc" && o.name === layer.name);
|
||||
return found !== undefined;
|
||||
})(),
|
||||
actionOn: (e) => {
|
||||
let moc = A.MOCFromURL(layer.url + '/Moc.fits', {lineWidth: 3, name: layer.name});
|
||||
self.aladin.addMOC(moc);
|
||||
|
||||
self.mocHiPSUrls[layer.url] = moc;
|
||||
loadMOCBtn.update({tooltip: {content: 'Remove coverage', position: {direction: 'bottom'}}})
|
||||
|
||||
if (self.aladin.statusBar) {
|
||||
self.aladin.statusBar.appendMessage({
|
||||
message: 'Coverage of ' + layer.name + ' loaded',
|
||||
duration: 2000,
|
||||
type: 'info'
|
||||
})
|
||||
}
|
||||
},
|
||||
actionOff: (e) => {
|
||||
let moc = self.mocHiPSUrls[layer.url];
|
||||
self.aladin.removeLayer(moc)
|
||||
|
||||
delete self.mocHiPSUrls[layer.url];
|
||||
|
||||
loadMOCBtn.update({tooltip: {content: 'Add coverage', position: {direction: 'bottom'}}})
|
||||
|
||||
if (self.aladin.statusBar) {
|
||||
self.aladin.statusBar.appendMessage({
|
||||
message: 'Coverage of ' + layer.name + ' removed',
|
||||
duration: 2000,
|
||||
type: 'info'
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let layerClassName = 'a' + layer.layer.replace(/[.\/ ]/g, '')
|
||||
|
||||
let btns = [showBtn, editBtn];
|
||||
|
||||
if (layer.subtype !== 'fits') {
|
||||
btns.push(loadMOCBtn)
|
||||
}
|
||||
btns.push(deleteBtn)
|
||||
|
||||
let item = Layout.horizontal({
|
||||
layout: [
|
||||
'<div class="' + layerClassName + '" style="background-color: rgba(0, 0, 0, 0.6); line-height: 1rem; padding: 3px; border-radius: 3px; word-break: break-word;' + (selectedLayer === layer.layer ? 'border: 1px solid white;' : '') + '">' + (layer.name) + '</div>',
|
||||
Layout.horizontal({layout: btns})
|
||||
],
|
||||
/*cssStyle: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
listStyle: 'none',
|
||||
justifyContent: 'space-between',
|
||||
width: '100%',
|
||||
}*/
|
||||
});
|
||||
|
||||
let l = {
|
||||
label: item,
|
||||
classList: 'surveyItem',
|
||||
cssStyle,
|
||||
hover(e) {
|
||||
showBtn.el.style.visibility = 'visible'
|
||||
editBtn.el.style.visibility = 'visible'
|
||||
deleteBtn.el.style.visibility = 'visible'
|
||||
loadMOCBtn.el.style.visibility = 'visible'
|
||||
},
|
||||
unhover(e) {
|
||||
showBtn.el.style.visibility = 'hidden'
|
||||
editBtn.el.style.visibility = 'hidden'
|
||||
deleteBtn.el.style.visibility = 'hidden'
|
||||
loadMOCBtn.el.style.visibility = 'hidden'
|
||||
}
|
||||
};
|
||||
|
||||
l.subMenu = [];
|
||||
|
||||
for(const [id, ll] of defaultLayers) {
|
||||
backgroundUrl = OverlayStack.previewImagesUrl[ll.name];
|
||||
if (!backgroundUrl) {
|
||||
backgroundUrl = ll.url + '/preview.jpg'
|
||||
}
|
||||
let cssStyle = {
|
||||
height: '2.5em',
|
||||
};
|
||||
if (backgroundUrl) {
|
||||
cssStyle = {
|
||||
backgroundSize: '100%',
|
||||
backgroundImage: 'url(' + backgroundUrl + ')',
|
||||
...cssStyle
|
||||
}
|
||||
}
|
||||
|
||||
l.subMenu.push({
|
||||
//selected: layer.name === aladin.getBaseImageLayer().name,
|
||||
label: '<div style="background-color: rgba(0, 0, 0, 0.6); line-height: 1rem; padding: 3px; border-radius: 3px">' + ll.name + '</div>',
|
||||
cssStyle,
|
||||
action(e) {
|
||||
self.aladin.setOverlayImageLayer(id, layer.layer);
|
||||
},
|
||||
hover(e, item) {
|
||||
item.style.filter = 'brightness(1.5)';
|
||||
},
|
||||
unhover(e, item) {
|
||||
item.style.filter = 'brightness(1.0)';
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
l.subMenu.push({
|
||||
label: {
|
||||
icon: {
|
||||
url: searchIconImg,
|
||||
monochrome: true,
|
||||
tooltip: {content: 'Find a specific survey <br /> in our database...', position: { direction: 'top' }},
|
||||
cssStyle: {
|
||||
cursor: 'help',
|
||||
},
|
||||
},
|
||||
content: 'More...'
|
||||
},
|
||||
action(o) {
|
||||
o.stopPropagation();
|
||||
o.preventDefault();
|
||||
|
||||
self._hide();
|
||||
|
||||
self.hipsBox = new HiPSSelectorBox(self.aladin)
|
||||
|
||||
self.hipsBox.attach(
|
||||
(HiPSId) => {
|
||||
self.aladin.setOverlayImageLayer(HiPSId, layer.layer);
|
||||
self.show();
|
||||
}
|
||||
);
|
||||
|
||||
self.hipsBox._show({
|
||||
position: self.position,
|
||||
})
|
||||
|
||||
self.mode = 'hips';
|
||||
}
|
||||
})
|
||||
|
||||
l.action = (o) => {
|
||||
let oldLayerClassName = 'a' + self.aladin.getSelectedLayer().replace(/[.\/ ]/g, '')
|
||||
self.el.querySelector('.' + oldLayerClassName).style.removeProperty('border')
|
||||
self.aladin.selectLayer(layer.layer);
|
||||
self.el.querySelector('.' + layerClassName).style.border = '1px solid white';
|
||||
}
|
||||
|
||||
layout.push(l);
|
||||
}
|
||||
|
||||
super.attach(layout);
|
||||
}
|
||||
|
||||
_findPreviewImageUrl(layer) {
|
||||
if (layer instanceof ImageFITS) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!layer.creatorDid) {
|
||||
return;
|
||||
}
|
||||
|
||||
const creatorDid = layer.creatorDid;
|
||||
|
||||
for (const key in Stack.previewImagesUrl) {
|
||||
if (creatorDid.includes(key)) {
|
||||
return Stack.previewImagesUrl[key];
|
||||
}
|
||||
}
|
||||
// if not found
|
||||
return layer.url + '/preview.jpg'
|
||||
}
|
||||
|
||||
_addOverlayIcon(overlay) {
|
||||
var tooltipText;
|
||||
var svg = '';
|
||||
if (overlay.type == 'catalog' || overlay.type == 'progressivecat') {
|
||||
var nbSources = overlay.getSources().length;
|
||||
tooltipText = nbSources + ' source' + (nbSources > 1 ? 's' : '');
|
||||
|
||||
svg = Icon.SVG_ICONS.CATALOG;
|
||||
}
|
||||
else if (overlay.type == 'moc') {
|
||||
tooltipText = 'Coverage: ' + (100 * overlay.skyFraction()).toFixed(2) + ' % of sky';
|
||||
|
||||
svg = Icon.SVG_ICONS.MOC;
|
||||
}
|
||||
else if (overlay.type == 'overlay') {
|
||||
svg = Icon.SVG_ICONS.OVERLAY;
|
||||
}
|
||||
|
||||
let tooltip;
|
||||
if (tooltipText) {
|
||||
tooltip = { content: tooltipText, position: {direction: 'bottom'} }
|
||||
}
|
||||
|
||||
// retrieve SVG icon, and apply the layer color
|
||||
return new Icon({
|
||||
size: 'small',
|
||||
url: Icon.dataURLFromSVG({svg, color: overlay.color}),
|
||||
tooltip
|
||||
});
|
||||
}
|
||||
|
||||
show(options) {
|
||||
if (this.mode !== 'stack') {
|
||||
if(this.hipsSelectorBox) {
|
||||
this.hipsSelectorBox.remove()
|
||||
}
|
||||
|
||||
if(this.catBox) {
|
||||
this.catBox.remove()
|
||||
}
|
||||
|
||||
if(this.hipsBox) {
|
||||
this.hipsBox.remove()
|
||||
}
|
||||
|
||||
if(this.editBox) {
|
||||
this.editBox.remove()
|
||||
}
|
||||
}
|
||||
|
||||
self.mode = 'stack';
|
||||
|
||||
this.attach();
|
||||
|
||||
this.position = (options && options.position) || this.position || { anchor: 'center center'};
|
||||
this.position.aladin = this.aladin;
|
||||
super.show({
|
||||
...options,
|
||||
...{position: this.position},
|
||||
cssStyle: {
|
||||
maxWidth: '17rem',
|
||||
}
|
||||
})
|
||||
|
||||
const innerHeight = this.aladin.aladinDiv.offsetHeight;
|
||||
this.element().querySelectorAll(".surveyItem")
|
||||
.forEach((surveyItem) => {
|
||||
surveyItem.querySelectorAll(".aladin-context-sub-menu")
|
||||
// skip the first menu
|
||||
.forEach((subMenu) => {
|
||||
subMenu.style.display = 'block'
|
||||
|
||||
let Y = innerHeight - (subMenu.getBoundingClientRect().y - this.aladin.aladinDiv.getBoundingClientRect().y);
|
||||
subMenu.style.display = 'none'
|
||||
|
||||
subMenu.style.maxHeight = Y + 'px';
|
||||
subMenu.style.overflowY = 'scroll';
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -42,25 +42,29 @@ export class FoV extends DOMElement {
|
||||
// constructor
|
||||
constructor(aladin, options) {
|
||||
let layout = [];
|
||||
|
||||
|
||||
if (options.showZoomControl) {
|
||||
let zoomIn = new ActionButton({
|
||||
classList: 'aladin-zoom-in',
|
||||
layout.push(new ActionButton({
|
||||
size: 'small',
|
||||
tooltip: {content: 'zoom in', position: {direction: 'top'}},
|
||||
icon: {
|
||||
monochrome: true,
|
||||
size: 'small',
|
||||
url: plusIconUrl,
|
||||
},
|
||||
cssStyle: {
|
||||
marginRight: 0,
|
||||
borderRight: 'none',
|
||||
borderRadius: '5px 0px 0px 5px'
|
||||
},
|
||||
action(o) {
|
||||
aladin.increaseZoom();
|
||||
}
|
||||
})
|
||||
let zoomOut = new ActionButton({
|
||||
}))
|
||||
layout.push(new ActionButton({
|
||||
size: 'small',
|
||||
classList: 'aladin-zoom-out',
|
||||
tooltip: {content: 'zoom out', position: {direction: 'top'}},
|
||||
cssStyle: {
|
||||
borderRadius: '0px 5px 5px 0px'
|
||||
},
|
||||
icon: {
|
||||
monochrome: true,
|
||||
size: 'small',
|
||||
@@ -69,12 +73,7 @@ export class FoV extends DOMElement {
|
||||
action(o) {
|
||||
aladin.decreaseZoom();
|
||||
}
|
||||
});
|
||||
zoomIn.el.classList.add('aladin-zoom-in');
|
||||
zoomOut.el.classList.add('aladin-zoom-out');
|
||||
|
||||
layout.push(zoomIn)
|
||||
layout.push(zoomOut)
|
||||
}))
|
||||
}
|
||||
|
||||
if (options.showFov) {
|
||||
@@ -83,10 +82,10 @@ export class FoV extends DOMElement {
|
||||
'<div class="aladin-monospace-text"></div>'])
|
||||
}
|
||||
|
||||
let el = Layout.horizontal({layout});
|
||||
if (el) {
|
||||
el.addClass('aladin-fov');
|
||||
el.addClass('aladin-dark-theme')
|
||||
let el = Layout.horizontal({layout, tooltip: { content: 'FoV', position: {direction: "top"}}});
|
||||
if (el.tooltip) {
|
||||
el.tooltip.addClass('aladin-fov');
|
||||
el.tooltip.addClass('aladin-dark-theme')
|
||||
}
|
||||
|
||||
super(el)
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
// Copyright 2013 - UDS/CNRS
|
||||
// The Aladin Lite program is distributed under the terms
|
||||
// of the GNU General Public License version 3.
|
||||
//
|
||||
// This file is part of Aladin Lite.
|
||||
//
|
||||
// Aladin Lite is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 3 of the License.
|
||||
//
|
||||
// Aladin Lite is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// The GNU General Public License is available in COPYING file
|
||||
// along with Aladin Lite.
|
||||
//
|
||||
|
||||
import { Box } from "../Widgets/Box.js";
|
||||
import { Layout } from "../Layout.js";
|
||||
import { ActionButton } from "../Widgets/ActionButton.js";
|
||||
import { ALEvent } from "../../events/ALEvent.js";
|
||||
import { Icon } from "../Widgets/Icon.js";
|
||||
import infoIconUrl from '../../../../assets/icons/info.svg';
|
||||
|
||||
/******************************************************************************
|
||||
* Aladin Lite project
|
||||
*
|
||||
* File gui/HiPSSelector.js
|
||||
*
|
||||
*
|
||||
* Author: Thomas Boch, Matthieu Baumann[CDS]
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
// Copyright 2013 - UDS/CNRS
|
||||
// The Aladin Lite program is distributed under the terms
|
||||
// of the GNU General Public License version 3.
|
||||
//
|
||||
// This file is part of Aladin Lite.
|
||||
//
|
||||
// Aladin Lite is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 3 of the License.
|
||||
//
|
||||
// Aladin Lite is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// The GNU General Public License is available in COPYING file
|
||||
// along with Aladin Lite.
|
||||
//
|
||||
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Aladin Lite project
|
||||
*
|
||||
* File Location.js
|
||||
*
|
||||
* Author: Thomas Boch[CDS]
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
import { Input } from "../Widgets/Input.js";
|
||||
|
||||
export class Dropdown extends Input {
|
||||
|
||||
// constructor
|
||||
constructor(aladin, options) {
|
||||
let self;
|
||||
aladin.view.catalogCanvas.addEventListener('click', (e) => {
|
||||
self.el.blur();
|
||||
});
|
||||
|
||||
options.options = options.options || [];
|
||||
|
||||
super({
|
||||
type: 'text',
|
||||
autocomplete: {options: options.options},
|
||||
...options
|
||||
})
|
||||
this.el.classList.add('search')
|
||||
|
||||
self = this;
|
||||
this._addListeners(aladin);
|
||||
}
|
||||
|
||||
update(options) {
|
||||
let newOptions = {};
|
||||
|
||||
if (options && options.options) {
|
||||
newOptions['autocomplete'] = {options: options.options};
|
||||
|
||||
delete options.options;
|
||||
}
|
||||
|
||||
// add the other input text options
|
||||
newOptions = {...newOptions, ...options};
|
||||
super.update(newOptions)
|
||||
}
|
||||
|
||||
_addListeners(aladin) {
|
||||
}
|
||||
};
|
||||
@@ -106,6 +106,13 @@ export class Layout extends DOMElement {
|
||||
return layout;
|
||||
}
|
||||
|
||||
static toolbar(options, target, position = "beforeend") {
|
||||
let layout = new Layout(options, target, position);
|
||||
layout.addClass('aladin-toolbar');
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append an item at the beginning
|
||||
* @param {DOMElement} item - Represents the structure of the Tabs
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
import { CooConversion } from "../CooConversion.js";
|
||||
|
||||
import { Coo } from "../libs/astro/coo.js";
|
||||
import { CooFrameEnum } from "../CooFrameEnum.js";
|
||||
|
||||
@@ -76,11 +76,10 @@ export class Location extends DOMElement {
|
||||
keydown: (e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
field.removeClass('aladin-not-valid'); // remove red border
|
||||
field.removeClass('aladin-valid'); // remove red border
|
||||
field.removeClass('aladin-unknownObject'); // remove red border
|
||||
|
||||
if (e.key === 'Enter') {
|
||||
//field.el.blur();
|
||||
field.el.blur();
|
||||
|
||||
let object = field.get();
|
||||
|
||||
@@ -91,14 +90,12 @@ export class Location extends DOMElement {
|
||||
object,
|
||||
{
|
||||
error: function () {
|
||||
field.addClass('aladin-not-valid');
|
||||
field.addClass('aladin-unknownObject');
|
||||
field.update({placeholder: object + ' not found...'})
|
||||
field.set('');
|
||||
field.el.focus();
|
||||
},
|
||||
success: function() {
|
||||
field.addClass('aladin-valid');
|
||||
|
||||
field.update({placeholder:'Search for an object...', value: object});
|
||||
}
|
||||
}
|
||||
@@ -149,15 +146,7 @@ export class Location extends DOMElement {
|
||||
let param = e.detail;
|
||||
|
||||
if (param.type === 'mouseout') {
|
||||
let radec = aladin.getRaDec();
|
||||
// convert to the view frame
|
||||
let lonlat = radec;
|
||||
if (aladin.getFrame() === "Galactic") {
|
||||
lonlat = CooConversion.J2000ToGalactic(radec)
|
||||
}
|
||||
|
||||
let [lon, lat] = lonlat;
|
||||
self.field.el.blur()
|
||||
let [lon, lat] = aladin.getRaDec();
|
||||
self.update({
|
||||
lon, lat,
|
||||
frame: aladin.view.cooFrame,
|
||||
@@ -165,10 +154,6 @@ export class Location extends DOMElement {
|
||||
}, aladin);
|
||||
}
|
||||
|
||||
if(param.state.dragging) {
|
||||
self.field.el.blur()
|
||||
}
|
||||
|
||||
if (param.type === 'mousemove' && param.state.dragging === false) {
|
||||
if (focused) {
|
||||
return;
|
||||
@@ -184,7 +169,6 @@ export class Location extends DOMElement {
|
||||
});
|
||||
|
||||
ALEvent.POSITION_CHANGED.listenedBy(aladin.aladinDiv, function (e) {
|
||||
|
||||
self.update({
|
||||
lon: e.detail.lon,
|
||||
lat: e.detail.lat,
|
||||
@@ -228,8 +212,7 @@ export class Location extends DOMElement {
|
||||
else {
|
||||
self.field.set(coo.format('d/'))
|
||||
}
|
||||
self.field.removeClass('aladin-not-valid');
|
||||
self.field.removeClass('aladin-valid');
|
||||
self.field.removeClass('aladin-unknownObject');
|
||||
|
||||
self.field.element().style.color = options.isViewCenter ? aladin.getReticle().getColor() : 'white';
|
||||
//self.field.el.blur()
|
||||
|
||||
@@ -15,4 +15,28 @@ Element.prototype.swap = function (node) {
|
||||
|
||||
// Move `node` to before the sibling of `this`
|
||||
parent.insertBefore(node, sibling);
|
||||
};
|
||||
};
|
||||
|
||||
export let Utils = {}
|
||||
/**
|
||||
* Append el to target
|
||||
*
|
||||
* target must be an DOM Element/Node
|
||||
*
|
||||
* @API
|
||||
*
|
||||
* @param el: el can be a Widget or Element object. Otherwise it is considered as text
|
||||
* @param target: target must be an DOM Element/Node
|
||||
*
|
||||
*/
|
||||
Utils.binarySearch = function(array, value) {
|
||||
var low = 0,
|
||||
high = array.length;
|
||||
|
||||
while (low < high) {
|
||||
var mid = (low + high) >>> 1;
|
||||
if (array[mid] < value) low = mid + 1;
|
||||
else high = mid;
|
||||
}
|
||||
return low;
|
||||
}
|
||||
@@ -167,10 +167,6 @@ export class ActionButton extends DOMElement {
|
||||
super._show();
|
||||
}
|
||||
|
||||
click() {
|
||||
this.el.click()
|
||||
}
|
||||
|
||||
static createIconBtn(opt, target, position = 'beforeend') {
|
||||
let btn = new ActionButton({...opt, size: 'medium'}, target, position);
|
||||
|
||||
|
||||
@@ -50,10 +50,11 @@ export class Box extends DOMElement {
|
||||
//el.style.display = "initial";
|
||||
|
||||
super(el, options);
|
||||
this._show();
|
||||
|
||||
this.addClass('aladin-dark-theme')
|
||||
|
||||
this.attachTo(target, position);
|
||||
this._show();
|
||||
this.addClass('aladin-dark-theme')
|
||||
}
|
||||
|
||||
_show(options) {
|
||||
@@ -66,30 +67,6 @@ export class Box extends DOMElement {
|
||||
|
||||
let self = this;
|
||||
|
||||
let close = this.options.close === false ? false : true;
|
||||
let draggable = false;
|
||||
if (close) {
|
||||
new ActionButton({
|
||||
size: 'small',
|
||||
content: '❌',
|
||||
//tooltip: {content: 'Close the window', position: {direction: 'bottom'}},
|
||||
action(e) {
|
||||
self._hide();
|
||||
},
|
||||
cssStyle: {
|
||||
position: 'absolute',
|
||||
},
|
||||
position: {
|
||||
top: 0,
|
||||
right: 0,
|
||||
}
|
||||
}, this.el);
|
||||
}
|
||||
|
||||
if (this.options.onDragged) {
|
||||
draggable = true;
|
||||
}
|
||||
|
||||
// Check for the title
|
||||
if (this.options.header) {
|
||||
let header = this.options.header;
|
||||
@@ -103,10 +80,6 @@ export class Box extends DOMElement {
|
||||
|
||||
let draggableEl;
|
||||
if (header.draggable) {
|
||||
draggable = true;
|
||||
}
|
||||
|
||||
if (draggable) {
|
||||
draggableEl = new ActionButton({
|
||||
icon: {
|
||||
url: moveIconImg,
|
||||
@@ -119,13 +92,30 @@ export class Box extends DOMElement {
|
||||
},
|
||||
action(e) {}
|
||||
});
|
||||
|
||||
dragElement(draggableEl.element(), this.el)
|
||||
dragElement(titleEl, this.el)
|
||||
titleEl.style.cursor = 'move'
|
||||
}
|
||||
|
||||
let headerEl = Layout.horizontal([draggableEl, titleEl], this.el);
|
||||
if (draggable) {
|
||||
dragElement(headerEl.element(), this.el, this.options.onDragged);
|
||||
headerEl.element().style.cursor = 'move';
|
||||
}
|
||||
let closedEl = new ActionButton({
|
||||
size: 'small',
|
||||
content: '❌',
|
||||
tooltip: {content: 'Close the window', position: {direction: 'bottom'}},
|
||||
cssStyle: {
|
||||
cursor: 'pointer',
|
||||
},
|
||||
action(e) {
|
||||
self._hide();
|
||||
}
|
||||
});
|
||||
|
||||
Layout.horizontal({
|
||||
cssStyle: {
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
layout: [draggableEl, titleEl, closedEl]
|
||||
}, this.el);
|
||||
|
||||
let separatorEl = document.createElement('div')
|
||||
separatorEl.classList.add("aladin-box-separator");
|
||||
@@ -138,11 +128,12 @@ export class Box extends DOMElement {
|
||||
|
||||
if (this.options.content) {
|
||||
let content = this.options.content
|
||||
//if (Array.isArray(content)) {
|
||||
|
||||
if (Array.isArray(content)) {
|
||||
this.appendContent(new Layout({layout: content}));
|
||||
} else {
|
||||
this.appendContent(content);
|
||||
//} else {
|
||||
// this.appendContent(content);
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.position) {
|
||||
@@ -158,10 +149,10 @@ export class Box extends DOMElement {
|
||||
}
|
||||
|
||||
// Heavily inspired from https://www.w3schools.com/howto/howto_js_draggable.asp
|
||||
function dragElement(triggerElt, elmnt, onDragged) {
|
||||
function dragElement(triggerElt, elmnt) {
|
||||
var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
|
||||
// otherwise, move the DIV from anywhere inside the DIV:
|
||||
var t, l;
|
||||
|
||||
triggerElt.onmousedown = dragMouseDown;
|
||||
|
||||
function dragMouseDown(e) {
|
||||
@@ -173,10 +164,6 @@ function dragElement(triggerElt, elmnt, onDragged) {
|
||||
document.onmouseup = closeDragElement;
|
||||
// call a function whenever the cursor moves:
|
||||
document.onmousemove = elementDrag;
|
||||
|
||||
if (onDragged) {
|
||||
onDragged();
|
||||
}
|
||||
}
|
||||
|
||||
function elementDrag(e) {
|
||||
@@ -187,37 +174,14 @@ function dragElement(triggerElt, elmnt, onDragged) {
|
||||
pos2 = pos4 - e.clientY;
|
||||
pos3 = e.clientX;
|
||||
pos4 = e.clientY;
|
||||
|
||||
// set the element's new position:
|
||||
t = elmnt.offsetTop - pos2
|
||||
l = elmnt.offsetLeft - pos1
|
||||
elmnt.style.top = t + "px";
|
||||
elmnt.style.left = l + "px";
|
||||
elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
|
||||
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
|
||||
}
|
||||
|
||||
function closeDragElement() {
|
||||
// stop moving when mouse button is released:
|
||||
document.onmouseup = null;
|
||||
document.onmousemove = null;
|
||||
|
||||
var r = elmnt.getBoundingClientRect();
|
||||
|
||||
if (t < r.height / 2) {
|
||||
elmnt.style.top = r.height / 2 + "px";
|
||||
}
|
||||
|
||||
if (l < r.width / 2) {
|
||||
elmnt.style.left = r.width / 2 + "px";
|
||||
}
|
||||
|
||||
const aladinDiv = elmnt.closest('.aladin-container');
|
||||
|
||||
if (l + r.width / 2 > aladinDiv.offsetWidth) {
|
||||
elmnt.style.left = (aladinDiv.offsetWidth - r.width / 2) + "px";
|
||||
}
|
||||
|
||||
if (t + r.height / 2 > aladinDiv.offsetHeight) {
|
||||
elmnt.style.top = (aladinDiv.offsetHeight - r.height / 2) + "px";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,12 +121,11 @@ export class ContextMenu extends DOMElement {
|
||||
monochrome: true,
|
||||
url: copyIconUrl,
|
||||
size: 'small',
|
||||
cssStyle: {
|
||||
cursor: 'not-allowed',
|
||||
}
|
||||
tooltip: {content: 'Copy the position!', position: {direction: 'bottom'}}
|
||||
}),
|
||||
posStr
|
||||
]).attachTo(item)
|
||||
|
||||
} catch (e) {
|
||||
item.innerHTML = '<span>Out of projection</span>';
|
||||
}
|
||||
@@ -241,13 +240,11 @@ export class ContextMenu extends DOMElement {
|
||||
|
||||
if (!opt.disabled || opt.disabled === false) {
|
||||
if (!opt.subMenu || opt.subMenu.length === 0) {
|
||||
let close = opt.action(e, self);
|
||||
|
||||
close = close !== undefined ? close : true;
|
||||
|
||||
if (close && ((opt.mustHide === undefined || opt.mustHide === true) && (!self.options || self.options.hideOnClick === undefined || self.options.hideOnClick === true))) {
|
||||
if ((opt.mustHide === undefined || opt.mustHide === true) && (!self.options || self.options.hideOnClick === undefined || self.options.hideOnClick === true)) {
|
||||
self._hide();
|
||||
}
|
||||
|
||||
opt.action(e, self);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -53,13 +53,9 @@ Exemple of layout object
|
||||
export class Form extends DOMElement {
|
||||
constructor(options, target, position = "beforeend") {
|
||||
let el = document.createElement('form');
|
||||
el.onsubmit = (e) => {
|
||||
e.preventDefault();
|
||||
};
|
||||
el.className = "aladin-form";
|
||||
|
||||
super(el, options);
|
||||
|
||||
this.attachTo(target, position)
|
||||
|
||||
this._show()
|
||||
@@ -71,7 +67,7 @@ export class Form extends DOMElement {
|
||||
let layout = [];
|
||||
if (this.options && this.options.subInputs) {
|
||||
this.options.subInputs.forEach(subInput => {
|
||||
layout.push(this._createInput(subInput))
|
||||
layout.push(Form._createInput(subInput))
|
||||
});
|
||||
}
|
||||
|
||||
@@ -91,27 +87,21 @@ export class Form extends DOMElement {
|
||||
);
|
||||
}
|
||||
|
||||
this.appendContent(Layout.vertical(layout))
|
||||
this.appendContent(new Layout(layout))
|
||||
super._show();
|
||||
}
|
||||
|
||||
_createInput(layout) {
|
||||
if (layout instanceof DOMElement || !layout.subInputs) {
|
||||
let input;
|
||||
let label = document.createElement('label');
|
||||
if (layout instanceof DOMElement) {
|
||||
input = layout;
|
||||
label.textContent = input.options.label;
|
||||
} else {
|
||||
input = new Input(layout);
|
||||
|
||||
if (layout.labelContent) {
|
||||
DOMElement.appendTo(layout.labelContent, label);
|
||||
} else {
|
||||
label.textContent = layout.label;
|
||||
}
|
||||
}
|
||||
static _createInput(layout) {
|
||||
if (!layout.subInputs) {
|
||||
let input = new Input(layout);
|
||||
|
||||
let label = document.createElement('label');
|
||||
if (layout.labelContent) {
|
||||
DOMElement.appendTo(layout.labelContent, label);
|
||||
} else {
|
||||
label.textContent = layout.label;
|
||||
}
|
||||
|
||||
label.for = input.el.id;
|
||||
|
||||
let item = new Layout([label, input]);
|
||||
@@ -125,7 +115,7 @@ export class Form extends DOMElement {
|
||||
}
|
||||
|
||||
layout.subInputs.map((subInput) => {
|
||||
let input = this._createInput(subInput)
|
||||
let input = Form._createInput(subInput)
|
||||
groupLayout.push(input)
|
||||
});
|
||||
|
||||
|
||||
@@ -103,6 +103,9 @@ export class Icon extends DOMElement {
|
||||
if (this.options.url) {
|
||||
let img = document.createElement('img');
|
||||
img.src = this.options.url;
|
||||
img.style.objectFit = 'contain';
|
||||
img.style.verticalAlign = 'middle';
|
||||
img.style.width = '100%';
|
||||
|
||||
this.el.appendChild(img);
|
||||
}
|
||||
@@ -153,9 +156,6 @@ export class Icon extends DOMElement {
|
||||
elt.querySelector('svg').setAttribute('width', size);
|
||||
elt.querySelector('svg').setAttribute('height', size);
|
||||
|
||||
elt.style.width = size;
|
||||
elt.style.height = size;
|
||||
|
||||
return elt.innerHTML;
|
||||
};
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
import { DOMElement } from "./Widget";
|
||||
import { Tooltip } from "./Tooltip";
|
||||
import { Utils } from "../../Utils";
|
||||
|
||||
/******************************************************************************
|
||||
* Aladin Lite project
|
||||
*
|
||||
@@ -60,9 +59,9 @@ export class Input extends DOMElement {
|
||||
|
||||
super(el, options);
|
||||
|
||||
this.attachTo(target, position)
|
||||
this.target = target;
|
||||
this._show()
|
||||
|
||||
this.attachTo(target, position)
|
||||
}
|
||||
|
||||
_show() {
|
||||
@@ -106,95 +105,7 @@ export class Input extends DOMElement {
|
||||
this.el.step = "any";
|
||||
}
|
||||
|
||||
let self = this;
|
||||
|
||||
|
||||
function logPositionMinMax(value, minp, maxp) {
|
||||
// position will be between 1 / 3600 and 1.0
|
||||
var minv = Math.log(minp);
|
||||
var maxv = Math.log(maxp);
|
||||
|
||||
// calculate adjustment factor
|
||||
var scale = (maxv-minv) / (maxp-minp);
|
||||
console.log('value', value)
|
||||
|
||||
return (Math.log(value)-minv) / scale + minp;
|
||||
}
|
||||
|
||||
function logPosition(value) {
|
||||
// position will be between 1 / 3600 and 1.0
|
||||
var minp = self.options.min; // 1 arcsec
|
||||
var maxp = self.options.max; // 1 deg
|
||||
|
||||
var minv = Math.log(self.options.min);
|
||||
var maxv = Math.log(self.options.max);
|
||||
|
||||
// calculate adjustment factor
|
||||
var scale = (maxv-minv) / (maxp-minp);
|
||||
console.log(minv, maxv)
|
||||
|
||||
return (Math.log(value)-minv) / scale + minp;
|
||||
}
|
||||
|
||||
function logSliderMinMax(position, minp, maxp) {
|
||||
|
||||
var minv = Math.log(minp);
|
||||
var maxv = Math.log(maxp);
|
||||
|
||||
// calculate adjustment factor
|
||||
var scale = (maxv-minv) / (maxp-minp);
|
||||
|
||||
return Math.exp(minv + scale*(position-minp));
|
||||
}
|
||||
|
||||
function logSlider(position) {
|
||||
// position will be between 1 / 3600 and 1.0
|
||||
var minp = self.options.min; // 1 arcsec
|
||||
var maxp = self.options.max; // 1 deg
|
||||
|
||||
var minv = Math.log(self.options.min);
|
||||
var maxv = Math.log(self.options.max);
|
||||
|
||||
// calculate adjustment factor
|
||||
var scale = (maxv-minv) / (maxp-minp);
|
||||
|
||||
return Math.exp(minv + scale*(position-minp));
|
||||
}
|
||||
|
||||
if (this.options.type === "range") {
|
||||
if (this.options.reversed === true) {
|
||||
this.addClass('aladin-reversed');
|
||||
}
|
||||
|
||||
if (this.options.stretch) {
|
||||
let stretch = this.options.stretch || 'linear';
|
||||
if (stretch === 'log') {
|
||||
// Refers to this StackOverflow post: https://stackoverflow.com/questions/846221/logarithmic-slider
|
||||
|
||||
|
||||
|
||||
if (this.options.ticks) {
|
||||
this.options.ticks = this.options.ticks.map((t) => logPosition(t));
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (this.options.change) {
|
||||
let change = this.options.change;
|
||||
this.options.change = (e) => {
|
||||
const value = logSlider(e.target.value)
|
||||
/*let p = 100 * (e.target.value - this.options.min) / (this.options.max - this.options.min);
|
||||
if (this.options.reversed === true) {
|
||||
p = 100 - p;
|
||||
}
|
||||
this.el.style.background = 'linear-gradient(to right, lightgreen ' + p + '%, #bababa ' + p + '%)';
|
||||
*/
|
||||
change(e, this, value);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.ticks) {
|
||||
this.options.autocomplete = {options: this.options.ticks};
|
||||
delete this.options.ticks;
|
||||
@@ -208,57 +119,23 @@ export class Input extends DOMElement {
|
||||
if (this.options.autocomplete) {
|
||||
const autocomplete = this.options.autocomplete
|
||||
if (autocomplete instanceof Object && autocomplete !== null) {
|
||||
let datalist = null;
|
||||
if (this.el.parentNode) {
|
||||
datalist = this.el.parentNode.querySelector('#' + 'ticks-' + this.options.name);
|
||||
}
|
||||
|
||||
if (datalist) {
|
||||
// it has been found, remove what's inside it
|
||||
datalist.innerHTML = "";
|
||||
} else {
|
||||
// it has not been found, then create it
|
||||
if (this.options.type === 'range') {
|
||||
datalist = document.createElement('datalist');
|
||||
} else {
|
||||
datalist = document.createElement('datalist');
|
||||
}
|
||||
datalist.id = 'ticks-' + this.options.name;
|
||||
if (this.options.type === "range")
|
||||
datalist.classList.add('aladin-input-range-datalist')
|
||||
|
||||
// and insert it into the dom
|
||||
this.el.appendChild(datalist);
|
||||
|
||||
this.el.setAttribute('list', datalist.id);
|
||||
}
|
||||
let datalist = document.createElement('datalist');
|
||||
|
||||
autocomplete.options.forEach((o) => {
|
||||
|
||||
let optionEl;
|
||||
if (this.options.type === 'range') {
|
||||
optionEl = document.createElement('option');
|
||||
let p = (o - this.options.min) / (this.options.max - this.options.min);
|
||||
optionEl.value = o;
|
||||
|
||||
const lerp = (x, min, max) => {
|
||||
return x * max + (1 - x) * min;
|
||||
};
|
||||
|
||||
if (this.options.reversed) {
|
||||
p = 1 - p;
|
||||
}
|
||||
|
||||
optionEl.style.left = 'calc(' + 100.0 * p + '% + ' + lerp(p, 0.5, -0.5) + 'rem)';
|
||||
} else {
|
||||
optionEl = document.createElement('option');
|
||||
optionEl.value = o;
|
||||
}
|
||||
|
||||
datalist.appendChild(optionEl);
|
||||
let option = document.createElement('option');
|
||||
option.value = o;
|
||||
datalist.appendChild(option);
|
||||
})
|
||||
|
||||
this.el.autocomplete = 'off';
|
||||
datalist.id = 'ticks-' + this.options.name;
|
||||
this.el.setAttribute('list', datalist.id);
|
||||
|
||||
if (this.el.querySelector('#' + datalist.id)) {
|
||||
this.el.querySelector('#' + datalist.id).remove()
|
||||
}
|
||||
this.el.appendChild(datalist);
|
||||
|
||||
this.el.autocomplete = 'on';
|
||||
} else {
|
||||
this.el.autocomplete = autocomplete;
|
||||
}
|
||||
@@ -286,17 +163,13 @@ export class Input extends DOMElement {
|
||||
if (this.options.change) {
|
||||
if (this.options.type === 'color' || this.options.type === 'range' || this.options.type === "text") {
|
||||
this.el.removeEventListener('input', this.action);
|
||||
|
||||
this.action = (e) => {
|
||||
this.options.change(e, this);
|
||||
};
|
||||
|
||||
this.el.addEventListener('input', this.action);
|
||||
} else {
|
||||
this.el.removeEventListener('change', this.action);
|
||||
|
||||
this.action = this.options.change;
|
||||
|
||||
this.el.addEventListener('change', this.action);
|
||||
}
|
||||
}
|
||||
@@ -326,10 +199,6 @@ export class Input extends DOMElement {
|
||||
this.el.name = this.options.name;
|
||||
}
|
||||
|
||||
if (this.options.title) {
|
||||
this.el.title = this.options.title;
|
||||
}
|
||||
|
||||
this.el.classList.add('aladin-input');
|
||||
this.el.classList.add('aladin-dark-theme');
|
||||
|
||||
@@ -354,16 +223,9 @@ export class Input extends DOMElement {
|
||||
super._show()
|
||||
}
|
||||
|
||||
attachTo(target, position = 'beforeend') {
|
||||
super.attachTo(target, position);
|
||||
|
||||
if (this.options.type === "range") {
|
||||
// Dirty workaround for plotting the slider ticks
|
||||
// The input slider must have a parent so that
|
||||
// its datalist can be put into the DOM
|
||||
this._show()
|
||||
}
|
||||
}
|
||||
/*setPlaceholder(placeholder) {
|
||||
this.el.placeholder = placeholder;
|
||||
}*/
|
||||
|
||||
update(options) {
|
||||
// if no options given, use the previous one set
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
import { Utils } from "../Utils.js";
|
||||
import { DOMElement } from "./Widget.js";
|
||||
|
||||
export class Table extends DOMElement {
|
||||
@@ -81,7 +82,6 @@ export class Table extends DOMElement {
|
||||
let val = row.data[key] || '--';
|
||||
tdEl.innerHTML = val;
|
||||
tdEl.classList.add("aladin-text-td-container");
|
||||
tdEl.title = val;
|
||||
}
|
||||
|
||||
trEl.appendChild(tdEl);
|
||||
|
||||
@@ -136,17 +136,15 @@ export class Tooltip extends DOMElement {
|
||||
}
|
||||
|
||||
if (this.options.content) {
|
||||
let content = [].concat(this.options.content);
|
||||
for (var c of content) {
|
||||
if (c instanceof DOMElement) {
|
||||
c.attachTo(tooltipEl)
|
||||
} else if (c instanceof Element) {
|
||||
tooltipEl.insertAdjacentElement('beforeend', c);
|
||||
} else {
|
||||
let wrapEl = document.createElement('div');
|
||||
wrapEl.innerHTML = c;
|
||||
tooltipEl.insertAdjacentElement('beforeend', wrapEl);
|
||||
}
|
||||
let content = this.options.content;
|
||||
if (content instanceof DOMElement) {
|
||||
content.attachTo(tooltipEl)
|
||||
} else if (content instanceof Element) {
|
||||
tooltipEl.insertAdjacentElement('beforeend', content);
|
||||
} else {
|
||||
let wrapEl = document.createElement('div');
|
||||
wrapEl.innerHTML = content;
|
||||
tooltipEl.insertAdjacentElement('beforeend', wrapEl);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -146,9 +146,9 @@ export class DOMElement {
|
||||
}
|
||||
|
||||
const aladinDiv = options && options.aladin && options.aladin.aladinDiv;
|
||||
let innerWidth = aladinDiv && aladinDiv.offsetWidth;
|
||||
let innerHeight = aladinDiv && aladinDiv.offsetHeight;
|
||||
|
||||
if (!aladinDiv) {
|
||||
return;
|
||||
}
|
||||
|
||||
let left, top, bottom, right;
|
||||
let x, y;
|
||||
@@ -156,8 +156,11 @@ export class DOMElement {
|
||||
// handle the anchor/dir case with higher priority
|
||||
const {offsetWidth, offsetHeight} = el;
|
||||
|
||||
const innerWidth = aladinDiv.offsetWidth;
|
||||
const innerHeight = aladinDiv.offsetHeight;
|
||||
|
||||
// take on less priority the left and top
|
||||
if (options && (options.left !== undefined || options.top !== undefined || options.right !== undefined || options.bottom !== undefined)) {
|
||||
if (options && (options.left || options.top || options.right || options.bottom)) {
|
||||
el.style.position = 'absolute';
|
||||
|
||||
if (options.top !== undefined) {
|
||||
@@ -174,7 +177,7 @@ export class DOMElement {
|
||||
}
|
||||
|
||||
if (typeof top === 'number') {
|
||||
if (innerHeight && top + offsetHeight >= innerHeight) {
|
||||
if (top + offsetHeight >= innerHeight) {
|
||||
y = '-' + (top + offsetHeight - innerHeight) + 'px';
|
||||
} else if (top < 0) {
|
||||
y = Math.abs(top) + 'px';
|
||||
@@ -186,7 +189,7 @@ export class DOMElement {
|
||||
bottom = bottom + 'px';
|
||||
}
|
||||
if (typeof left === 'number') {
|
||||
if (innerWidth && left + offsetWidth > innerWidth) {
|
||||
if (left + offsetWidth > innerWidth) {
|
||||
x = '-' + (left + offsetWidth - innerWidth) + 'px';
|
||||
} else if (left < 0) {
|
||||
x = Math.abs(left) + 'px';
|
||||
@@ -301,7 +304,7 @@ export class DOMElement {
|
||||
attachTo(target, position = 'beforeend') {
|
||||
if(target) {
|
||||
if (typeof position === 'number') {
|
||||
target.insertBefore(this.element(), target.childNodes[position]);
|
||||
target.insertChildAtIndex(this.element(), position)
|
||||
} else {
|
||||
target.insertAdjacentElement(position, this.element());
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user