From 107eedaffe548554f7c5d2fc3f80fe97ea08c92e Mon Sep 17 00:00:00 2001 From: Matthieu Baumann Date: Wed, 20 Dec 2023 11:33:31 +0100 Subject: [PATCH] working on a jsdoc formatted api documentation --- examples/al-displayJPG.html | 4 +- src/js/A.js | 43 -- src/js/Aladin.js | 796 +++++++++++++++++++++--------------- src/js/AladinUtils.js | 19 +- src/js/Catalog.js | 2 +- src/js/ColorCfg.js | 2 +- src/js/ImageFITS.js | 13 - src/js/ImageSurvey.js | 192 +++++++-- src/js/View.js | 31 +- 9 files changed, 668 insertions(+), 434 deletions(-) diff --git a/examples/al-displayJPG.html b/examples/al-displayJPG.html index db635cec..08bf2b24 100644 --- a/examples/al-displayJPG.html +++ b/examples/al-displayJPG.html @@ -39,7 +39,9 @@ Image Opacity:
"TAN" (Gnomonic projection) + *
"STG" (Stereographic projection) + *
"SIN" (Orthographic projection) + *
"ZEA" (Zenital equal-area projection) + *
"FEYE" (Fish eye projection) + *
"AIR" (Airy projection) + *
"ARC" (Zenital equidistant projection) + *
"NCP" (North celestial pole projection) + *
"MER" (Mercator projection) + *
"CAR" (Plate Carrée projection) + *
"CEA" (Cylindrical equal area projection) + *
"CYP" (Cylindrical perspective projection) + *
"AIT" (Hammer-Aitoff projection) + *
"PAR" (Parabolic projection) + *
"SFL" (Sanson-Flamsteed projection) + *
"MOL" (Mollweide projection) + *
"COD" (Conic equidistant projection) + *
"HPX" (Healpix projection) + * + * @example + * // Set the projection to 'orthographic' + * let aladin = A.aladin('#aladin-lite-div'); + * aladin.setProjection('SIN'); + */ Aladin.prototype.setProjection = function (projection) { if (!projection) { return; @@ -700,22 +748,43 @@ export let Aladin = (function () { return projName; };`` - /** return the current coordinate system: possible values are 'J2000', 'J2000d', and 'Galactic' - * @api + /** + * Returns the current coordinate system: possible values are 'J2000', 'J2000d', and 'Galactic' . * + * @memberof Aladin + * @returns {string} The current coordinate system: possible values are 'J2000', 'J2000d', and 'Galactic' . + * + * @example + * const aladin = A.aladin('#aladin-lite-div', {cooFrame: 'galactic'}); + * let cooFrame = aladin.getFrame(); + * assert(cooFrame, 'galactic') */ Aladin.prototype.getFrame = function() { return this.view.cooFrame.label; } - /** point view to a given object (resolved by Sesame) or position - * @api + /** + * Moves the Aladin instance to the specified astronomical object. * - * @param: target; object name or position - * @callbackOptions: (optional) the object with key 'success' and/or 'error' containing the success and error callback functions. + * @memberof Aladin + * @param {string} targetName - The name or identifier of the astronomical object to move to. + * @param {Object} [callbackOptions] - Optional callback options. + * @param {function} [callbackOptions.success] - The callback function to execute on successful navigation. + * @param {function} [callbackOptions.error] - The callback function to execute on error during navigation. * + * @example + * // Move to the astronomical object named 'M42' with callbacks + * const aladinInstance = A.aladin('#aladin-lite-div'); + * aladinInstance.gotoObject('M42', { + * success: () => { + * console.log('Successfully moved to M42.'); + * }, + * error: (err) => { + * console.error('Error moving to M42:', err); + * } + * }); */ - Aladin.prototype.gotoObject = function (targetName, callbackOptions, options) { + Aladin.prototype.gotoObject = function (targetName, callbackOptions) { let successCallback = undefined; let errorCallback = undefined; if (typeof callbackOptions === 'object') { @@ -740,7 +809,7 @@ export let Aladin = (function () { coo.parse(targetName); // Convert from view coo sys to icrs const [ra, dec] = this.wasm.viewToICRSCooSys(coo.lon, coo.lat); - this.view.pointTo(ra, dec, options); + this.view.pointTo(ra, dec); (typeof successCallback === 'function') && successCallback(this.getRaDec()); } @@ -758,7 +827,7 @@ export let Aladin = (function () { function (data) { // success callback // Location given in icrs at J2000 const coo = data.coo; - self.view.pointTo(coo.jradeg, coo.jdedeg, options); + self.view.pointTo(coo.jradeg, coo.jdedeg); (typeof successCallback === 'function') && successCallback(self.getRaDec()); }, @@ -776,7 +845,7 @@ export let Aladin = (function () { const body = baseImageLayer.properties.hipsBody; PlanetaryFeaturesNameResolver.resolve(targetName, body, function (data) { // success callback - self.view.pointTo(data.lon, data.lat, options); + self.view.pointTo(data.lon, data.lat); (typeof successCallback === 'function') && successCallback(self.getRaDec()); }, @@ -793,27 +862,43 @@ export let Aladin = (function () { } }; - - - /** - * go to a given position, expressed in the current coordinate frame + /** + * Moves the Aladin instance to the specified position. * - * @API + * @memberof Aladin + * @param {number} lon - longitude in degrees + * @param {number} lat - latitude in degrees + * @param {string} frame - Optional callback options. + * + * @example + * // Move to position + * const aladin = A.aladin('#aladin-lite-div'); + * aladin.gotoPosition(20, 10, "galactic"); */ - Aladin.prototype.gotoPosition = function (lon, lat) { + Aladin.prototype.gotoPosition = function (lon, lat, frame = undefined) { var radec; - // first, convert to J2000 if needed - if (this.view.cooFrame == CooFrameEnum.GAL) { - radec = CooConversion.GalacticToJ2000([lon, lat]); + // convert the frame from string to CooFrameEnum + if (frame) { + frame = CooFrameEnum.fromString(this.options.cooFrame, CooFrameEnum.J2000); } - else { + // both are CooFrameEnum + let positionGivenFrame = frame || this.view.cooFrame; + // First, convert to J2000 if needed + if (positionGivenFrame === CooFrameEnum.GAL) { + radec = CooConversion.GalacticToJ2000([lon, lat]); + } else { radec = [lon, lat]; } + this.view.pointTo(radec[0], radec[1]); }; - + var idTimeoutAnim; var doAnimation = function (aladin) { + if (idTimeoutAnim) { + clearTimeout(idTimeoutAnim) + } + var params = aladin.animationParams; if (params == null || !params['running']) { return; @@ -840,8 +925,7 @@ export let Aladin = (function () { aladin.gotoRaDec(curRa, curDec); - setTimeout(function () { doAnimation(aladin); }, 10); - + idTimeoutAnim = setTimeout(function () { doAnimation(aladin); }, 10); }; /* @@ -985,9 +1069,14 @@ export let Aladin = (function () { /** - * get current [ra, dec] position of the center of the view + * Gets the current [Right Ascension, Declination] position of the center of the Aladin view. * - * @API + * This method returns the celestial coordinates of the center of the Aladin view in the International + * Celestial Reference System (ICRS) or J2000 equatorial coordinates. + * + * @memberof Aladin + * @returns {number[]} - An array representing the [Right Ascension, Declination] coordinates in degrees. + * The first element is the Right Ascension (RA), and the second element is the Declination (Dec). */ Aladin.prototype.getRaDec = function () { let radec = this.wasm.getCenter(); // This is given in the frame of the view @@ -1032,11 +1121,7 @@ export let Aladin = (function () { return this.reticle; }; - Aladin.prototype.removeLayers = function () { - this.view.removeLayers(); - }; - - // these 3 methods should be merged into a unique "add" method + // these 4 methods should be merged into a unique "add" method Aladin.prototype.addCatalog = function (catalog) { this.view.addCatalog(catalog); @@ -1068,6 +1153,10 @@ export let Aladin = (function () { return result[0]; } + Aladin.prototype.removeLayers = function () { + this.view.removeLayers(); + }; + // @API Aladin.prototype.removeLayer = function(layer) { this.view.removeLayer(layer); @@ -1330,6 +1419,7 @@ export let Aladin = (function () { Aladin.prototype.selectObjects = function(objects) { this.view.selectObjects(objects) }; + // Possible values are 'rect', 'poly' and 'circle' Aladin.prototype.select = async function (mode = 'rect', callback) { await this.reticle.loaded; @@ -1368,10 +1458,25 @@ export let Aladin = (function () { }; /** - * Change the coo grid options - * @param {Object} options options of the coordinate grid - * @param {Boolean} options.enable enable or disable the grid - * @param {String} options.color color of the form 'rgba(255, 0, 255, 255)' + * Sets the coordinate grid options for the Aladin Lite view. + * + * This method allows you to customize the appearance of the coordinate grid in the Aladin Lite view. + * + * @memberof Aladin + * + * @param {Object} options - Options to customize the coordinate grid. + * @param {string} [options.color] - The color of the coordinate grid. + * @param {number} [options.opacity] - The opacity of the coordinate grid (value between 0 and 1). + * @param {number} [options.labelSize] - The size of the coordinate grid labels in pixels. + * @param {number} [options.thickness] - The thickness of the coordinate grid lines. + * @param {boolean} [options.enabled] - If true, the coordinate grid is enabled; otherwise, it is disabled. + * + * @example + * // Set the coordinate grid color to red + * aladin.setCooGrid({ color: 'red' }); + * + * // Enable the coordinate grid + * aladin.setCooGrid({ enabled: true }); */ Aladin.prototype.setCooGrid = function(options) { if (options.color) { @@ -1408,8 +1513,6 @@ export let Aladin = (function () { return null; }; - - // TODO : integrate somehow into API ? Aladin.prototype.exportAsPNG = function (downloadFile = false) { (async () => { @@ -1560,96 +1663,77 @@ export let Aladin = (function () { }; /** - * Transform pixel coordinates to world coordinates + * Transform pixel coordinates to world coordinates. * - * Origin (0,0) of pixel coordinates is at top left corner of Aladin Lite view + * The origin (0,0) of pixel coordinates is at the top-left corner of the Aladin Lite view. * - * @API + * @memberof Aladin * - * @param x - * @param y - * - * @return a [ra, dec] array with world coordinates in degrees. Returns undefined is something went wrong + * @param {number} x - The x-coordinate in pixel coordinates. + * @param {number} y - The y-coordinate in pixel coordinates. * + * @returns {number[]} - An array representing the [Right Ascension, Declination] coordinates in degrees. + * + * @throws {Error} Throws an error if an issue occurs during the transformation. */ Aladin.prototype.pix2world = function (x, y) { - // this might happen at early stage of initialization - if (!this.view) { - return undefined; + const [ra, dec] = this.wasm.screenToWorld(x, y); + + if (ra < 0) { + return [ra + 360.0, dec]; } - try { - const [ra, dec] = this.wasm.screenToWorld(x, y); - - if (ra < 0) { - return [ra + 360.0, dec]; - } - - return [ra, dec]; - } catch (e) { - return undefined; - } + return [ra, dec]; }; /** - * Transform world coordinates to pixel coordinates in the view + * Transform world coordinates to pixel coordinates in the view. * - * @API + * @memberof Aladin * - * @param ra - * @param dec + * @param {number} ra - The Right Ascension (RA) coordinate in degrees. + * @param {number} dec - The Declination (Dec) coordinate in degrees. * - * @return a [x, y] array with pixel coordinates in the view. Returns null if the projection failed somehow + * @returns {number[]} - An array representing the [x, y] coordinates in pixel coordinates in the view. * + * @throws {Error} Throws an error if an issue occurs during the transformation. */ Aladin.prototype.world2pix = function (ra, dec) { - // this might happen at early stage of initialization - if (!this.view) { - return; - } - - try { - return this.wasm.worldToScreen(ra, dec); - } catch (e) { - return undefined; - } + return this.wasm.worldToScreen(ra, dec); }; - /** - * Transform world coordinates to pixel coordinates in the view + /** + * Get the angular distance in degrees between two locations * - * @API + * @memberof Aladin * - * @param ra - * @param dec + * @param {number} x1 - The x-coordinate of the first pixel coordinates. + * @param {number} y1 - The y-coordinate of the first pixel coordinates. + * @param {number} x2 - The x-coordinate of the second pixel coordinates. + * @param {number} y2 - The y-coordinate of the second pixel coordinates. * - * @return a [x, y] array with pixel coordinates in the view. Returns null if the projection failed somehow + * @returns {number} - The angular distance between the two pixel coordinates in degrees * + * @throws {Error} Throws an error if an issue occurs during the transformation. */ - Aladin.prototype.angularDist = function (x1, y1, x2, y2) { - // this might happen at early stage of initialization - if (!this.view) { - return; - } + Aladin.prototype.angularDist = function (x1, y1, x2, y2) { + const [ra1, dec1] = this.pix2world(x1, y1); + const [ra2, dec2] = this.pix2world(x2, y2); - try { - const [ra1, dec1] = this.pix2world(x1, y1); - const [ra2, dec2] = this.pix2world(x2, y2); - - return this.wasm.angularDist(ra1, dec1, ra2, dec2); - } catch (e) { - return undefined; - } + return this.wasm.angularDist(ra1, dec1, ra2, dec2); }; /** + * Gets a set of points along the current Field of View (FoV) corners. * - * @API + * @memberof Aladin * - * @param ra - * @param nbSteps the number of points to return along each side (the total number of points returned is 4*nbSteps) + * @param {number} nbSteps - The number of points to return along each side (the total number of points returned is 4 * nbSteps). * - * @return set of points along the current FoV with the following format: [[ra1, dec1], [ra2, dec2], ..., [ra_n, dec_n]] + * @returns {number[][]} - A set of positions along the current FoV with the following format: [[ra1, dec1], [ra2, dec2], ..., [ra_n, dec_n]]. + * The positions will be given in degrees + * + * @throws {Error} Throws an error if an issue occurs during the transformation. * */ Aladin.prototype.getFovCorners = function (nbSteps) { @@ -1677,9 +1761,12 @@ export let Aladin = (function () { }; /** - * @API + * Gets the current Field of View (FoV) size in degrees as a 2-element array. * - * @return the current FoV size in degrees as a 2-elements array + * @memberof Aladin + * + * @returns {number[]} - A 2-element array representing the current FoV size in degrees. The first element is the FoV width, + * and the second element is the FoV height. */ Aladin.prototype.getFov = function () { // can go up to 1000 deg @@ -1697,9 +1784,12 @@ export let Aladin = (function () { }; /** - * @API + * Returns the size in pixels for the Aladin view * - * @return the size in pixels of the Aladin Lite view + * @memberof Aladin + * + * @returns {number[]} - A 2-element array representing the current Aladin view size in pixels. The first element is the width, + * and the second element is the height. */ Aladin.prototype.getSize = function () { return [this.view.width, this.view.height]; @@ -1714,194 +1804,244 @@ export let Aladin = (function () { return this.aladinDiv; }; - return Aladin; -})(); + // @API + /* + * return a Box GUI element to insert content + */ + /*Aladin.prototype.box = function (options) { + var box = new Box(options); + box.$parentDiv.appendTo(this.aladinDiv); -// @API -/* - * return a Box GUI element to insert content - */ -/*Aladin.prototype.box = function (options) { - var box = new Box(options); - box.$parentDiv.appendTo(this.aladinDiv); + return box; + };*/ - return box; -};*/ + // @API + /* + * show popup at ra, dec position with given title and content + * + * If circleRadius, the corresponding circle will also be plotted + */ + Aladin.prototype.showPopup = function (ra, dec, title, content, circleRadius) { + this.view.catalogForPopup.removeAll(); + this.view.overlayForPopup.removeAll(); -// @API -/* - * show popup at ra, dec position with given title and content - * - * If circleRadius, the corresponding circle will also be plotted - */ -Aladin.prototype.showPopup = function (ra, dec, title, content, circleRadius) { - this.view.catalogForPopup.removeAll(); - this.view.overlayForPopup.removeAll(); - - let marker; - if (circleRadius !== undefined) { - this.view.overlayForPopup.add(A.circle(ra, dec, circleRadius, {fillColor: 'rgba(255, 0, 0, 0.2)'})); - marker = A.marker(ra, dec, { popupTitle: title, popupDesc: content, useMarkerDefaultIcon: true }); - } - else { - marker = A.marker(ra, dec, { popupTitle: title, popupDesc: content, useMarkerDefaultIcon: false }); - } - - this.view.catalogForPopup.addSources(marker); - - this.view.overlayForPopup.show(); - this.view.catalogForPopup.show(); - - this.view.popup.setTitle(title); - this.view.popup.setText(content); - - this.view.popup.setSource(marker); - this.view.popup.show(); -}; - -// @API -/* - * hide popup - */ -Aladin.prototype.hidePopup = function () { - this.view.popup.hide(); -}; - -// @API -/* - * return a URL allowing to share the current view - */ -Aladin.prototype.getShareURL = function () { - var radec = this.getRaDec(); - var coo = new Coo(); - coo.prec = 7; - coo.lon = radec[0]; - coo.lat = radec[1]; - - return Aladin.URL_PREVIEWER + '?target=' + encodeURIComponent(coo.format('s')) + - '&fov=' + this.getFov()[0].toFixed(2) + '&survey=' + encodeURIComponent(this.getBaseImageLayer().id || this.getBaseImageLayer().rootUrl); -}; - -// @API -/* - * return, as a string, the HTML embed code - */ -Aladin.prototype.getEmbedCode = function () { - var radec = this.getRaDec(); - var coo = new Coo(); - coo.prec = 7; - coo.lon = radec[0]; - coo.lat = radec[1]; - - var survey = this.getBaseImageLayer().id; - var fov = this.getFov()[0]; - let s = ''; - const NL = "\n"; - s += '
' + NL; - s += '' + NL; - s += ''; - - return s; -}; - -// @API -/* - * Creates remotely a HiPS from a FITS image URL and displays it - */ -Aladin.prototype.displayFITS = function ( - url, - options, - successCallback, - errorCallback, - layer = "base" -) { - successCallback = successCallback || ((ra, dec, fov, _) => { - this.gotoRaDec(ra, dec); - this.setFoV(fov); - }); - const imageFits = this.createImageFITS(url, url, options, successCallback, errorCallback); - return this.setOverlayImageLayer(imageFits, layer); -}; - -// @API -/* - * Creates remotely a HiPS from a JPEG or PNG image with astrometry info - * and display it - */ -Aladin.prototype.displayJPG = Aladin.prototype.displayPNG = function (url, options, successCallback, errorCallback) { - options = options || {}; - options.color = true; - options.label = "JPG/PNG image"; - options.outputFormat = 'png'; - - options = options || {}; - - var data = { url: url }; - if (options.color) { - data.color = true; - } - if (options.outputFormat) { - data.format = options.outputFormat; - } - if (options.order) { - data.order = options.order; - } - if (options.nocache) { - data.nocache = options.nocache; - } - let self = this; - - const request = ( url, params = {}, method = 'GET' ) => { - let options = { - method - }; - if ( 'GET' === method ) { - url += '?' + ( new URLSearchParams( params ) ).toString(); - } else { - options.body = JSON.stringify( params ); + let marker; + if (circleRadius !== undefined) { + this.view.overlayForPopup.add(A.circle(ra, dec, circleRadius, {fillColor: 'rgba(255, 0, 0, 0.2)'})); + marker = A.marker(ra, dec, { popupTitle: title, popupDesc: content, useMarkerDefaultIcon: true }); + } + else { + marker = A.marker(ra, dec, { popupTitle: title, popupDesc: content, useMarkerDefaultIcon: false }); } - return fetch( url, options ).then( response => response.json() ); + this.view.catalogForPopup.addSources(marker); + + this.view.overlayForPopup.show(); + this.view.catalogForPopup.show(); + + this.view.popup.setTitle(title); + this.view.popup.setText(content); + + this.view.popup.setSource(marker); + this.view.popup.show(); }; - const get = ( url, params ) => request( url, params, 'GET' ); - get('https://alasky.unistra.fr/cgi/fits2HiPS', data) - .then(async (response) => { - if (response.status != 'success') { - console.error('An error occured: ' + response.message); - if (errorCallback) { - errorCallback(response.message); - } - return; - } - var label = options.label || "FITS image"; - var meta = response.data.meta; + // @API + /* + * hide popup + */ + Aladin.prototype.hidePopup = function () { + this.view.popup.hide(); + }; - const survey = self.createImageSurvey(response.data.url, label, response.data.url); - self.setOverlayImageLayer(survey, "overlay"); + // @API + /* + * return a URL allowing to share the current view + */ + Aladin.prototype.getShareURL = function () { + var radec = this.getRaDec(); + var coo = new Coo(); + coo.prec = 7; + coo.lon = radec[0]; + coo.lat = radec[1]; - var transparency = (options && options.transparency) || 1.0; + return Aladin.URL_PREVIEWER + '?target=' + encodeURIComponent(coo.format('s')) + + '&fov=' + this.getFov()[0].toFixed(2) + '&survey=' + encodeURIComponent(this.getBaseImageLayer().id || this.getBaseImageLayer().rootUrl); + }; - var executeDefaultSuccessAction = true; - if (successCallback) { - executeDefaultSuccessAction = successCallback(meta.ra, meta.dec, meta.fov); - } - if (executeDefaultSuccessAction === true) { - self.wasm.setCenter(meta.ra, meta.dec); - self.setFoV(meta.fov); - } + // @API + /* + * return, as a string, the HTML embed code + */ + Aladin.prototype.getEmbedCode = function () { + var radec = this.getRaDec(); + var coo = new Coo(); + coo.prec = 7; + coo.lon = radec[0]; + coo.lat = radec[1]; - // TODO! set an image survey once the already loaded surveys - // are READY! Otherwise it can lead to some congestion and avoid - // downloading the base tiles of the other surveys loading! - // This has to be fixed in the backend but a fast fix is just to wait - // before setting a new image survey + var survey = this.getBaseImageLayer().url; + var fov = this.getFov()[0]; + let s = ''; + const NL = "\n"; + s += '
' + NL; + s += '' + NL; + s += ''; + + return s; + }; + +/** + * Display a JPEG image in the Aladin Lite view. + * + * @memberof Aladin + * + * @param {string} url - The URL of the JPEG image. + * @param {Object} options - Options to customize the display. Can include the following properties: + * @param {string} options.label - A label for the displayed image. + * @param {number} options.order - The desired HEALPix order format. + * @param {boolean} options.nocache - True if you want to disable the cache + * @param {number} options.transparency - Opacity of the image rendered in aladin lite. Between 0 and 1. + * @param {Function} successCallback - The callback function to be executed on a successful display. + * The callback gives the ra, dec, and fov of the image; + * @param {Function} errorCallback - The callback function to be executed if an error occurs during display. + * + * @example + * aladin.displayJPG( + * // the JPG to transform to HiPS + * 'https://noirlab.edu/public/media/archives/images/large/noirlab1912a.jpg', + * { + * transparency: 0.6, + * label: 'NOIRLab image' + * }, + * (ra, dec, fov) => { + * // your code here + * }) + *); + */ + Aladin.prototype.displayFITS = function ( + url, + options, + successCallback, + errorCallback, + layer = "base" + ) { + successCallback = successCallback || ((ra, dec, fov, _) => { + this.gotoRaDec(ra, dec); + this.setFoV(fov); }); -}; + const imageFits = this.createImageFITS(url, url, options, successCallback, errorCallback); + return this.setOverlayImageLayer(imageFits, layer); + }; -Aladin.prototype.setReduceDeformations = function (reduce) { - this.reduceDeformations = reduce; - this.view.requestRedraw(); -} +/** + * Display a JPEG image in the Aladin Lite view. + * + * @memberof Aladin + * + * @param {string} url - The URL of the JPEG image. + * @param {Object} options - Options to customize the display. Can include the following properties: + * @param {string} options.label - A label for the displayed image. + * @param {number} options.order - The desired HEALPix order format. + * @param {boolean} options.nocache - True if you want to disable the cache + * @param {number} options.transparency - Opacity of the image rendered in aladin lite. Between 0 and 1. + * @param {Function} successCallback - The callback function to be executed on a successful display. + * The callback gives the ra, dec, and fov of the image; + * @param {Function} errorCallback - The callback function to be executed if an error occurs during display. + * + * @example + * aladin.displayJPG( + * // the JPG to transform to HiPS + * 'https://noirlab.edu/public/media/archives/images/large/noirlab1912a.jpg', + * { + * transparency: 0.6, + * label: 'NOIRLab image' + * }, + * (ra, dec, fov) => { + * // your code here + * }) + *); + */ + Aladin.prototype.displayJPG = Aladin.prototype.displayPNG = function (url, options, successCallback, errorCallback) { + options = options || {}; + options.color = true; + options.label = options.label || "JPG/PNG image"; + options.outputFormat = 'png'; + + options = options || {}; + + var data = { url }; + if (options.color) { + data.color = true; + } + if (options.outputFormat) { + data.format = options.outputFormat; + } + if (options.order) { + data.order = options.order; + } + if (options.nocache) { + data.nocache = options.nocache; + } + let self = this; + + const request = ( url, params = {}, method = 'GET' ) => { + let options = { + method + }; + if ( 'GET' === method ) { + url += '?' + ( new URLSearchParams( params ) ).toString(); + } else { + options.body = JSON.stringify( params ); + } + + return fetch( url, options ).then( response => response.json() ); + }; + const get = ( url, params ) => request( url, params, 'GET' ); + + get('https://alasky.unistra.fr/cgi/fits2HiPS', data) + .then(async (response) => { + if (response.status != 'success') { + console.error('An error occured: ' + response.message); + if (errorCallback) { + errorCallback(response.message); + } + return; + } + var label = options.label; + var meta = response.data.meta; + + const survey = self.createImageSurvey(response.data.url, label, response.data.url); + self.setOverlayImageLayer(survey, "overlay"); + + var transparency = (options && options.transparency) || 1.0; + survey.setOpacity(transparency); + + var executeDefaultSuccessAction = true; + if (successCallback) { + executeDefaultSuccessAction = successCallback(meta.ra, meta.dec, meta.fov); + } + if (executeDefaultSuccessAction === true) { + self.wasm.setCenter(meta.ra, meta.dec); + self.setFoV(meta.fov); + } + + // TODO! set an image survey once the already loaded surveys + // are READY! Otherwise it can lead to some congestion and avoid + // downloading the base tiles of the other surveys loading! + // This has to be fixed in the backend but a fast fix is just to wait + // before setting a new image survey + }); + }; + + /* + Aladin.prototype.setReduceDeformations = function (reduce) { + this.reduceDeformations = reduce; + this.view.requestRedraw(); + } + */ + + return Aladin; +})(); diff --git a/src/js/AladinUtils.js b/src/js/AladinUtils.js index ef062b0d..cea2ee20 100644 --- a/src/js/AladinUtils.js +++ b/src/js/AladinUtils.js @@ -28,9 +28,15 @@ * *****************************************************************************/ -export let AladinUtils = (function() { +export let AladinUtils = { + + + HEALPix: { + vertices: function() { + + } + }, - return { /** * passage de xy projection à xy dans la vue écran * @param x @@ -40,6 +46,7 @@ export let AladinUtils = (function() { * @param largestDim largest dimension of the view * @returns position in the view */ + /* xyToView: function(x, y, width, height, largestDim, zoomFactor, round) { if (round==undefined) { // we round by default @@ -54,7 +61,7 @@ export let AladinUtils = (function() { else { return {vx: largestDim/2*(1+zoomFactor*x)-(largestDim-width)/2, vy: largestDim/2*(1+zoomFactor*y)-(largestDim-height)/2}; } - }, + },*/ /** * passage de xy dans la vue écran à xy projection @@ -66,9 +73,9 @@ export let AladinUtils = (function() { * @param zoomFactor * @returns position in xy projection */ - viewToXy: function(vx, vy, width, height, largestDim, zoomFactor) { + /*viewToXy: function(vx, vy, width, height, largestDim, zoomFactor) { return {x: ((2*vx+(largestDim-width))/largestDim-1)/zoomFactor, y: ((2*vy+(largestDim-height))/largestDim-1)/zoomFactor}; - }, + },*/ /** * convert a @@ -274,5 +281,3 @@ export let AladinUtils = (function() { } }; - -})(); \ No newline at end of file diff --git a/src/js/Catalog.js b/src/js/Catalog.js index 2c0adf52..020c5463 100644 --- a/src/js/Catalog.js +++ b/src/js/Catalog.js @@ -42,7 +42,7 @@ import A from "./A.js"; * Represents a catalog with configurable options for display and interaction. * * @namespace - * @typedef {Object} Aladin + * @typedef {Object} Catalog */ export let Catalog = (function() { /** diff --git a/src/js/ColorCfg.js b/src/js/ColorCfg.js index a7cc25ab..964ff554 100644 --- a/src/js/ColorCfg.js +++ b/src/js/ColorCfg.js @@ -183,7 +183,7 @@ // @api // Optional arguments, - ColorCfg.prototype.setColormap = function(colormap = "native", options = {}) { + ColorCfg.prototype.setColormap = function(colormap = "native", options) { /// colormap // Make it case insensitive let cmap = formatColormap(colormap); diff --git a/src/js/ImageFITS.js b/src/js/ImageFITS.js index 7d18c264..140b06ee 100644 --- a/src/js/ImageFITS.js +++ b/src/js/ImageFITS.js @@ -70,10 +70,6 @@ export let ImageFITS = (function () { this.query = Promise.resolve(self); } - ImageFITS.prototype.isReady = function() { - return this.added; - } - // @api ImageFITS.prototype.setOpacity = function (opacity) { let self = this; @@ -250,15 +246,6 @@ export let ImageFITS = (function () { return false; } - // @api - ImageFITS.prototype.focusOn = function () { - // ensure the fits have been parsed - if (this.added) { - this.view.aladin.gotoRaDec(this.ra, this.dec); - this.view.aladin.setFoV(this.fov); - } - }; - // @oldapi ImageFITS.prototype.setAlpha = ImageFITS.prototype.setOpacity; diff --git a/src/js/ImageSurvey.js b/src/js/ImageSurvey.js index 7aa309cb..f9fa2218 100644 --- a/src/js/ImageSurvey.js +++ b/src/js/ImageSurvey.js @@ -29,7 +29,6 @@ *****************************************************************************/ import { Utils } from "./Utils"; import { ALEvent } from "./events/ALEvent.js"; -import { CooFrameEnum } from "./CooFrameEnum.js" import { ColorCfg } from "./ColorCfg.js"; import { ImageLayer } from "./ImageLayer.js"; import { HiPSProperties } from "./HiPSProperties.js"; @@ -56,11 +55,8 @@ PropertyParser.frame = function(options, properties = {}) { frame = "ICRS"; } else if (frame == "galactic") { frame = "GAL"; - } else if (frame === undefined) { - frame = "ICRS"; - console.warn('No cooframe given. Coordinate systems supported: "ICRS", "ICRSd", "j2000" or "galactic". ICRS is chosen by default'); } else { - frame = "ICRSd"; + frame = "ICRS"; console.warn('Invalid cooframe given: ' + cooFrame + '. Coordinate systems supported: "ICRS", "ICRSd", "j2000" or "galactic". ICRS is chosen by default'); } @@ -128,14 +124,42 @@ PropertyParser.isPlanetaryBody = function(options, properties = {}) { return properties.hips_body !== undefined; } +/** + * @typedef {Object} ImageSurveyOptions + * + * @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. + * @property {number} [maxOrder] - If not given, retrieved from the properties of the survey. + * @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 {string} [imgFormat] - If not given, look into the properties to see the accepted format. The format is chosen from PNG > WEBP > JPEG > FITS (in this importance order). + * @property {string} [cooFrame] - If not given, look into the properties. If it is a planet, then ICRS is chosen, otherwise its hips_frame key is read. If no value is found in the properties, ICRS is chosen by default. + */ export let ImageSurvey = (function () { - /** Constructor - * cooFrame and maxOrder can be set to null - * They will be determined by reading the properties file + /** + * The object describing an image survey * + * @class + * @constructs ImageSurvey + * + * @param {string} [id] - Optional, a uniq id for the survey. See {@link https://aladin.cds.unistra.fr/hips/list|here} for the list of IDs. + * Keep in mind that it is better to directly provide an url as it will not request our mocserver first to get final survey tiles retrieval url. + * @param {string} [name] - The name of the survey to be displayed in the UI + * @param {string} url - The url where the survey is located. Check the hips list {@link https://aladin.cds.unistra.fr/hips/list|here} for the valid survey urls to display. + * @param {ImageSurveyOptions} [options] - The option for the survey + * + * @description Prefer provide an url better than an id. If both are given, the url will be requested first for the survey data. */ function ImageSurvey(id, name, url, view, options) { - // A reference to the view this.view = view; this.wasm = view.wasm; this.added = false; @@ -179,7 +203,7 @@ export let ImageSurvey = (function () { // Request all the properties to see which mirror is the fastest HiPSProperties.getFasterMirrorUrl(properties) .then((url) => { - self.setUrl(url); + self._setUrl(url); }) .catch(e => { alert(e); @@ -373,11 +397,7 @@ export let ImageSurvey = (function () { })(); }; - ImageSurvey.prototype.isReady = function() { - return this.added; - } - - ImageSurvey.prototype.setUrl = function (url) { + ImageSurvey.prototype._setUrl = function (url) { if (this.properties.url !== url) { console.info("Change url of ", this.id, " from ", this.properties.url, " to ", url) @@ -389,13 +409,30 @@ export let ImageSurvey = (function () { this.properties.url = url; } } - + /** + * Checks if the ImageSurvey represents a planetary body. + * + * This method returns a boolean indicating whether the ImageSurvey corresponds to a planetary body, e.g. the earth or a celestial body. + * + * @memberof ImageSurvey + * + * @returns {boolean} Returns true if the ImageSurvey represents a planetary body; otherwise, returns false. + */ ImageSurvey.prototype.isPlanetaryBody = function() { return this.properties.isPlanetaryBody; } - // @api - // TODO: include imgFormat inside the ImageSurvey's meta attribute + /** + * Sets the image format for the ImageSurvey. + * + * This method updates the image format of the ImageSurvey, performs format validation, and triggers the update of metadata. + * + * @memberof ImageSurvey + * + * @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 ImageSurvey. + */ ImageSurvey.prototype.setImageFormat = function (format) { let self = this; self.query @@ -453,11 +490,24 @@ export let ImageSurvey = (function () { }) }; + /** + * Sets the opacity factor when rendering the ImageSurvey + * + * @memberof ImageSurvey + * + * @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. + */ ImageSurvey.prototype.getAvailableFormats = function() { return this.properties.formats; } - // @api + /** + * Sets the opacity factor when rendering the ImageSurvey + * + * @memberof ImageSurvey + * + * @param {number} opacity - Opacity of the survey to set. Between 0 and 1 + */ ImageSurvey.prototype.setOpacity = function (opacity) { let self = this; updateMetadata(self, () => { @@ -465,47 +515,139 @@ export let ImageSurvey = (function () { }); }; - // @api + /** + * Sets the blending mode when rendering the ImageSurvey + * + * @memberof ImageSurvey + * + * @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: + *
+ *
opacity * this_survey_color + (1 - opacity) * already_rendered_color for the default mode + *
opacity * this_survey_color + already_rendered_color for the additive mode + *
+ *
+ * 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] + */ ImageSurvey.prototype.setBlendingConfig = function (additive = false) { updateMetadata(this, () => { this.colorCfg.setBlendingConfig(additive); }); }; - // @api + /** + * Sets the colormap when rendering the ImageSurvey. + * + * @memberof ImageSurvey + * + * @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: + *
"blues" + *
"cividis" + *
"cubehelix" + *
"eosb" + *
"grayscale" + *
"inferno" + *
"magma" + *
"native" + *
"parula" + *
"plasma" + *
"rainbow" + *
"rdbu" + *
"rdylbu" + *
"redtemperature" + *
"sinebow" + *
"spectral" + *
"summer" + *
"viridis" + *
"ylgnbu" + *
"ylorbr" + *
"red" + *
"green" + *
"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. + */ ImageSurvey.prototype.setColormap = function (colormap, options) { updateMetadata(this, () => { this.colorCfg.setColormap(colormap, options); }); } - // @api + /** + * Sets the gamma correction factor for the ImageSurvey. + * + * This method updates the gamma of the ImageSurvey. + * + * @memberof ImageSurvey + * + * @param {number} lowCut - The low cut value to set for the ImageSurvey. + * @param {number} highCut - The high cut value to set for the ImageSurvey. + */ ImageSurvey.prototype.setCuts = function (lowCut, highCut) { updateMetadata(this, () => { this.colorCfg.setCuts(lowCut, highCut); }); }; - // @api + /** + * Sets the gamma correction factor for the ImageSurvey. + * + * This method updates the gamma of the ImageSurvey. + * + * @memberof ImageSurvey + * + * @param {number} gamma - The saturation value to set for the ImageSurvey. Between 0.1 and 10 + */ ImageSurvey.prototype.setGamma = function (gamma) { updateMetadata(this, () => { this.colorCfg.setGamma(gamma); }); }; - // @api + /** + * Sets the saturation for the ImageSurvey. + * + * This method updates the saturation of the ImageSurvey. + * + * @memberof ImageSurvey + * + * @param {number} saturation - The saturation value to set for the ImageSurvey. Between 0 and 1 + */ ImageSurvey.prototype.setSaturation = function (saturation) { updateMetadata(this, () => { this.colorCfg.setSaturation(saturation); }); }; + /** + * Sets the brightness for the ImageSurvey. + * + * This method updates the brightness of the ImageSurvey. + * + * @memberof ImageSurvey + * + * @param {number} brightness - The brightness value to set for the ImageSurvey. Between 0 and 1 + */ ImageSurvey.prototype.setBrightness = function (brightness) { updateMetadata(this, () => { this.colorCfg.setBrightness(brightness); }); }; + /** + * Sets the contrast for the ImageSurvey. + * + * This method updates the contrast of the ImageSurvey and triggers the update of metadata. + * + * @memberof ImageSurvey + * + * @param {number} contrast - The contrast value to set for the ImageSurvey. Between 0 and 1 + */ ImageSurvey.prototype.setContrast = function (contrast) { updateMetadata(this, () => { this.colorCfg.setContrast(contrast); @@ -521,7 +663,7 @@ export let ImageSurvey = (function () { } // Private method for updating the backend with the new meta - var updateMetadata = function (self, callback = undefined) { + var updateMetadata = function (self, callback) { if (callback) { callback(); } diff --git a/src/js/View.js b/src/js/View.js index 24b04e77..4a7cb46f 100644 --- a/src/js/View.js +++ b/src/js/View.js @@ -490,7 +490,7 @@ export let View = (function () { try { const lonlat = view.wasm.screenToWorld(xymouse.x, xymouse.y); var radec = view.wasm.viewToICRSCooSys(lonlat[0], lonlat[1]); - view.pointTo(radec[0], radec[1], { forceAnimation: true }); + view.pointTo(radec[0], radec[1]); } catch (err) { return; @@ -1320,17 +1320,19 @@ export let View = (function () { } }, a); - a.contextMenu.attach([ - { - label: Layout.horizontal([sampBtn, a.samp ? 'Send selection to SAMP' : 'SAMP disabled']), - }, - { - label: 'Remove selection', - action(o) { - a.view.unselectObjects(); + if (a.contextMenu) { + a.contextMenu.attach([ + { + label: Layout.horizontal([sampBtn, a.samp ? 'Send selection to SAMP' : 'SAMP disabled']), + }, + { + label: 'Remove selection', + action(o) { + a.view.unselectObjects(); + } } - } - ]); + ]); + } } } @@ -1342,8 +1344,7 @@ export let View = (function () { // initialAccDelta must be consistent with fovDegrees here View.prototype.setZoom = function (fovDegrees) { fovDegrees = Math.min(fovDegrees, this.projection.fov); - console.log(fovDegrees) - + this.wasm.setFieldOfView(fovDegrees); this.updateZoomState(); }; @@ -1748,8 +1749,7 @@ export let View = (function () { * @param options * */ - View.prototype.pointTo = function (ra, dec, options) { - options = options || {}; + View.prototype.pointTo = function (ra, dec) { ra = parseFloat(ra); dec = parseFloat(dec); @@ -1775,6 +1775,7 @@ export let View = (function () { // hide the popup if it is open this.aladin.hidePopup(); }; + View.prototype.makeUniqLayerName = function (name) { if (!this.layerNameExists(name)) { return name;