@cquiroz/aladin-lite
Version:
AladinLite module
1,439 lines (1,150 loc) • 64.9 kB
JavaScript
// 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 View.js
*
* Author: Thomas Boch[CDS]
*
*****************************************************************************/
import CooFrameEnum from './CooFrameEnum';
import Circle from './Circle';
import CooConversion from './CooConversion';
import CooGrid from './CooGrid';
import Coo from './coo';
import Utils from './Utils';
import AladinUtils from './AladinUtils';
import TileBuffer from './TileBuffer';
import SpatialVector from './SpatialVector';
import { HpxImageSurvey } from './HpxImageSurvey';
import { HealpixIndex, ORDER_MAX } from './HealpixIndex';
import HealpixCache from './HealpixCache';
import HealpixGrid from './HealpixGrid';
import ProjectionEnum from './ProjectionEnum';
import SimbadPointer from './SimbadPointer'; // import Stats from 'stats.js';
import Popup from './Popup';
import { Projection } from './projection';
import Downloader from './Downloader';
import ColorMap from './ColorMap';
import Footprint from './Footprint';
import { catalog } from './A';
var View = function () {
/** Constructor */
function View(aladin, location, fovDiv, cooFrame, zoom) {
this.aladin = aladin;
this.options = aladin.options;
this.aladinDiv = this.aladin.aladinDiv;
this.popup = new Popup(this.aladinDiv, this);
this.createCanvases();
HealpixCache.init();
this.location = location;
this.fovDiv = fovDiv;
this.mustClearCatalog = true;
this.mustRedrawReticle = true;
this.mode = View.PAN;
this.minFOV = this.maxFOV = null; // by default, no restriction
this.healpixGrid = new HealpixGrid(this.imageCanvas);
if (cooFrame) {
this.cooFrame = cooFrame;
} else {
this.cooFrame = CooFrameEnum.GAL;
}
var lon, lat;
lon = lat = 0;
this.projectionMethod = ProjectionEnum.SIN;
this.projection = new Projection(lon, lat);
this.projection.setProjection(this.projectionMethod);
this.zoomLevel = 0;
this.zoomFactor = this.computeZoomFactor(this.zoomLevel);
this.viewCenter = {
lon: lon,
lat: lat
}; // position of center of view
if (zoom) {
this.setZoom(zoom);
} // current reference image survey displayed
this.imageSurvey = null; // current catalogs displayed
this.catalogs = []; // a dedicated catalog for the popup
var c = document.createElement('canvas');
c.width = c.height = 24;
var ctx = c.getContext('2d');
ctx.lineWidth = 6.0;
ctx.beginPath();
ctx.strokeStyle = '#eee';
ctx.arc(12, 12, 8, 0, 2 * Math.PI, true);
ctx.stroke();
ctx.lineWidth = 3.0;
ctx.beginPath();
ctx.strokeStyle = '#c38';
ctx.arc(12, 12, 8, 0, 2 * Math.PI, true);
ctx.stroke();
this.catalogForPopup = catalog({
shape: c,
sourceSize: 24
}); //this.catalogForPopup = A.catalog({sourceSize: 18, shape: 'circle', color: '#c38'});
this.catalogForPopup.hide();
this.catalogForPopup.setView(this); // overlays (footprints for instance)
this.overlays = []; // MOCs
this.mocs = []; // reference to all overlay layers (= catalogs + overlays + mocs)
this.allOverlayLayers = [];
this.tileBuffer = new TileBuffer(); // tile buffer is shared across different image surveys
this.fixLayoutDimensions();
this.curNorder = 1;
this.realNorder = 1;
this.curOverlayNorder = 1; // some variables for mouse handling
this.dragging = false;
this.dragx = null;
this.dragy = null;
this.needRedraw = true; // zoom pinching
this.pinchZoomParameters = {
isPinching: false,
// true if a pinch zoom is ongoing
initialFov: undefined,
initialDistance: undefined
};
this.downloader = new Downloader(this); // the downloader object is shared across all HpxImageSurveys
this.flagForceRedraw = false;
this.fadingLatestUpdate = null;
this.dateRequestRedraw = null;
this.showGrid = false; // coordinates grid
init(this); // listen to window resize and reshape canvases
this.resizeTimer = null;
var self = this;
window.addEventListener('resize', function () {
clearTimeout(self.resizeTimer);
self.resizeTimer = setTimeout(function () {
self.fixLayoutDimensions(self);
}, 100);
}); // in some contexts (Jupyter notebook for instance), the parent div changes little time after Aladin Lite creation
// this results in canvas dimension to be incorrect.
// The following line tries to fix this issue
setTimeout(function () {
var computedWidth = self.aladinDiv.clientWidth;
var computedHeight = self.aladinDiv.clientHeight;
if (self.width !== computedWidth || self.height === computedHeight) {
self.fixLayoutDimensions();
self.setZoomLevel(self.zoomLevel); // needed to force recomputation of displayed FoV
}
}, 1000);
} // different available modes
View.PAN = 0;
View.SELECT = 1;
View.TOOL_SIMBAD_POINTER = 2; // TODO: should be put as an option at layer level
View.DRAW_SOURCES_WHILE_DRAGGING = true;
View.DRAW_MOCS_WHILE_DRAGGING = true;
View.CALLBACKS_THROTTLE_TIME_MS = 100; // minimum time between two consecutive callback calls
// (re)create needed canvases
View.prototype.createCanvases = function () {
var aladin = this.aladinDiv;
aladin.querySelectorAll('.aladin-imageCanvas').forEach(item => item.parentNode.removeChild(item));
aladin.querySelectorAll('.aladin-catalogCanvas').forEach(item => item.parentNode.removeChild(item));
aladin.querySelectorAll('.aladin-reticleCanvas').forEach(item => item.parentNode.removeChild(item)); // a.find('.aladin-imageCanvas').remove();
// a.find('.aladin-catalogCanvas').remove();
// a.find('.aladin-reticleCanvas').remove();
// this.imageCanvas = $("<canvas class='aladin-imageCanvas'></canvas>").appendTo(this.aladinDiv)[0];
// this.catalogCanvas = $("<canvas class='aladin-catalogCanvas'></canvas>").appendTo(this.aladinDiv)[0];
// this.reticleCanvas = $("<canvas class='aladin-reticleCanvas'></canvas>").appendTo(this.aladinDiv)[0];
// canvas to draw the images
this.imageCanvas = document.createElement("canvas");
this.imageCanvas.classList.add('aladin-imageCanvas');
this.aladinDiv.appendChild(this.imageCanvas); // canvas to draw the catalogs
this.catalogCanvas = document.createElement("canvas");
this.catalogCanvas.classList.add('aladin-catalogCanvas');
this.aladinDiv.appendChild(this.catalogCanvas); // canvas to draw the reticle
this.reticleCanvas = document.createElement("canvas");
this.reticleCanvas.classList.add('aladin-reticleCanvas');
this.aladinDiv.appendChild(this.reticleCanvas);
}; // called at startup and when window is resized
View.prototype.fixLayoutDimensions = function () {
Utils.cssScale = undefined; // const computedWidth = self.aladinDiv.clientWidth;
// const computedHeight = self.aladinDiv.clienhHeight;
var computedWidth = this.aladinDiv.clientWidth;
var computedHeight = this.aladinDiv.clientHeight;
this.width = Math.max(computedWidth, 1);
this.height = Math.max(computedHeight, 1); // this prevents many problems when div size is equal to 0
this.cx = this.width / 2;
this.cy = this.height / 2;
this.largestDim = Math.max(this.width, this.height);
this.smallestDim = Math.min(this.width, this.height);
this.ratio = this.largestDim / this.smallestDim;
this.mouseMoveIncrement = 160 / this.largestDim; // reinitialize 2D context
this.imageCtx = this.imageCanvas.getContext("2d");
this.catalogCtx = this.catalogCanvas.getContext("2d");
this.reticleCtx = this.reticleCanvas.getContext("2d");
this.imageCtx.canvas.width = this.width;
this.catalogCtx.canvas.width = this.width;
this.reticleCtx.canvas.width = this.width;
this.imageCtx.canvas.height = this.height;
this.catalogCtx.canvas.height = this.height;
this.reticleCtx.canvas.height = this.height;
pixelateCanvasContext(this.imageCtx, this.aladin.options.pixelateCanvas); // change logo
if (!this.logoDiv) {
this.logoDiv = this.aladinDiv.querySelectorAll('.aladin-logo')[0];
}
if (this.width > 800) {
this.logoDiv.classList.remove('aladin-logo-small');
this.logoDiv.classList.add('aladin-logo-large');
this.logoDiv.style.width = '90px';
} else {
this.logoDiv.classList.add('aladin-logo-small');
this.logoDiv.classList.remove('aladin-logo-large');
this.logoDiv.style.width = '32px';
}
this.computeNorder();
this.requestRedraw();
};
var pixelateCanvasContext = function pixelateCanvasContext(ctx, pixelateFlag) {
var enableSmoothing = !pixelateFlag;
ctx.imageSmoothingEnabled = enableSmoothing;
ctx.webkitImageSmoothingEnabled = enableSmoothing;
ctx.mozImageSmoothingEnabled = enableSmoothing;
ctx.msImageSmoothingEnabled = enableSmoothing;
ctx.oImageSmoothingEnabled = enableSmoothing;
};
View.prototype.setMode = function (mode) {
this.mode = mode;
if (this.mode === View.SELECT) {
this.setCursor('crosshair');
} else if (this.mode === View.TOOL_SIMBAD_POINTER) {
this.popup.hide();
this.reticleCanvas.style.cursor = '';
this.reticleCanvas.classList.add('aladin-sp-cursor');
} else {
this.setCursor('default');
}
};
View.prototype.setCursor = function (cursor) {
if (this.reticleCanvas.style.cursor === cursor) {
return;
}
if (this.mode === View.TOOL_SIMBAD_POINTER) {
return;
}
this.reticleCanvas.style.cursor = cursor;
};
/**
* return dataURL string corresponding to the current view
*/
View.prototype.getCanvasDataURL = function (imgType, width, height) {
imgType = imgType || "image/png";
var c = document.createElement('canvas');
c.width = width || this.width;
c.height = height || this.height;
var ctx = c.getContext('2d');
ctx.drawImage(this.imageCanvas, 0, 0, c.width, c.height);
ctx.drawImage(this.catalogCanvas, 0, 0, c.width, c.height);
ctx.drawImage(this.reticleCanvas, 0, 0, c.width, c.height);
return c.toDataURL(imgType); //return c.toDataURL("image/jpeg", 0.01); // setting quality only works for JPEG (?)
};
/**
* Compute the FoV in degrees of the view and update mouseMoveIncrement
*
* @param view
* @returns FoV (array of 2 elements : width and height) in degrees
*/
View.prototype.computeFov = function (view) {
var fov = this.doComputeFov(view, view.zoomFactor);
view.mouseMoveIncrement = fov / view.imageCanvas.width;
return fov;
};
View.prototype.doComputeFov = function (view, zoomFactor) {
// if zoom factor < 1, we view 180°
var fov;
if (view.zoomFactor < 1) {
fov = 180;
} else {
// TODO : fov sur les 2 dimensions !!
// to compute FoV, we first retrieve 2 points at coordinates (0, view.cy) and (width-1, view.cy)
var xy1 = AladinUtils.viewToXy(0, view.cy, view.width, view.height, view.largestDim, zoomFactor);
var lonlat1 = view.projection.unproject(xy1.x, xy1.y);
var xy2 = AladinUtils.viewToXy(view.imageCanvas.width - 1, view.cy, view.width, view.height, view.largestDim, zoomFactor);
var lonlat2 = view.projection.unproject(xy2.x, xy2.y);
fov = new Coo(lonlat1.ra, lonlat1.dec).distance(new Coo(lonlat2.ra, lonlat2.dec));
}
return fov;
};
View.prototype.updateFovDiv = function (view) {
if (view.fovDiv) {
if (isNaN(view.fov)) {
view.fovDiv.innerHTML = "FoV:";
return;
} // update FoV value
var fovStr;
if (view.fov > 1) {
fovStr = Math.round(view.fov * 100) / 100 + "°";
} else if (view.fov * 60 > 1) {
fovStr = Math.round(view.fov * 60 * 100) / 100 + "'";
} else {
fovStr = Math.round(view.fov * 3600 * 100) / 100 + '"';
}
view.fovDiv.innerHTML = "FoV: ".concat(fovStr);
}
};
View.prototype.createListeners = function (view) {
var hasTouchEvents = false;
if ('ontouchstart' in window) {
hasTouchEvents = true;
} // various listeners
var onDblClick = function onDblClick(e) {
var xymouse = view.imageCanvas.relMouseCoords(e);
var xy = AladinUtils.viewToXy(xymouse.x, xymouse.y, view.width, view.height, view.largestDim, view.zoomFactor);
try {
var lonlat = view.projection.unproject(xy.x, xy.y);
} catch (err) {
return;
}
var radec = []; // convert to J2000 if needed
if (view.cooFrame.system === CooFrameEnum.SYSTEMS.GAL) {
radec = CooConversion.GalacticToJ2000([lonlat.ra, lonlat.dec]);
} else {
radec = [lonlat.ra, lonlat.dec];
}
view.pointTo(radec[0], radec[1]);
};
if (!hasTouchEvents) {
view.reticleCanvas.addEventListener("dblclick", onDblClick);
}
var mouseDownEvent = function mouseDownEvent(e) {
// zoom pinching
if (e.type === 'touchstart' && e.originalEvent && e.originalEvent.targetTouches && e.originalEvent.targetTouches.length === 2) {
view.dragging = false;
view.pinchZoomParameters.isPinching = true;
var fov = view.aladin.getFov();
view.pinchZoomParameters.initialFov = Math.max(fov[0], fov[1]);
view.pinchZoomParameters.initialDistance = Math.sqrt(Math.pow(e.originalEvent.targetTouches[0].clientX - e.originalEvent.targetTouches[1].clientX, 2) + Math.pow(e.originalEvent.targetTouches[0].clientY - e.originalEvent.targetTouches[1].clientY, 2));
return;
}
var xymouse = view.imageCanvas.relMouseCoords(e);
if (e.originalEvent && e.originalEvent.targetTouches) {
view.dragx = e.originalEvent.targetTouches[0].clientX;
view.dragy = e.originalEvent.targetTouches[0].clientY;
} else {
/*
view.dragx = e.clientX;
view.dragy = e.clientY;
*/
view.dragx = xymouse.x;
view.dragy = xymouse.y;
}
view.dragging = true;
if (view.mode === View.PAN) {
view.setCursor('move');
} else if (view.mode === View.SELECT) {
view.selectStartCoo = {
x: view.dragx,
y: view.dragy
};
}
return false; // to disable text selection
};
view.reticleCanvas.addEventListener("mousedown", mouseDownEvent);
view.reticleCanvas.addEventListener("touchstart", mouseDownEvent); //$(view.reticleCanvas).bind("mouseup mouseout touchend", function(e) {
var clickEvent = function clickEvent(e) {
// reacting on 'click' rather on 'mouseup' is more reliable when panning the view
if (e.type === 'touchend' && view.pinchZoomParameters.isPinching) {
view.pinchZoomParameters.isPinching = false;
view.pinchZoomParameters.initialFov = view.pinchZoomParameters.initialDistance = undefined;
return;
}
var wasDragging = view.realDragging === true;
var selectionHasEnded = view.mode === View.SELECT && view.dragging;
if (view.dragging) {
// if we were dragging, reset to default cursor
view.setCursor('default');
view.dragging = false;
if (wasDragging) {
view.realDragging = false; // call positionChanged one last time after dragging, with dragging: false
var posChangedFn = view.aladin.callbacksByEventName['positionChanged'];
if (typeof posChangedFn === 'function') {
var pos = view.aladin.pix2world(view.width / 2, view.height / 2);
if (pos !== undefined) {
posChangedFn({
ra: pos[0],
dec: pos[1],
dragging: false
});
}
}
}
} // end of "if (view.dragging) ... "
if (selectionHasEnded) {
view.aladin.fire('selectend', view.getObjectsInBBox(view.selectStartCoo.x, view.selectStartCoo.y, view.dragx - view.selectStartCoo.x, view.dragy - view.selectStartCoo.y));
view.mustRedrawReticle = true; // pour effacer selection bounding box
view.requestRedraw();
return;
}
view.mustClearCatalog = true;
view.mustRedrawReticle = true; // pour effacer selection bounding box
view.dragx = view.dragy = null;
if (e.type === "mouseout" || e.type === "touchend") {
view.requestRedraw(true);
updateLocation(view, view.width / 2, view.height / 2, true);
if (e.type === "mouseout") {
if (view.mode === View.TOOL_SIMBAD_POINTER) {
view.setMode(View.PAN);
}
return;
}
}
var xymouse = view.imageCanvas.relMouseCoords(e);
if (view.mode === View.TOOL_SIMBAD_POINTER) {
var radec = view.aladin.pix2world(xymouse.x, xymouse.y);
view.setMode(View.PAN);
view.setCursor('wait');
SimbadPointer.query(radec[0], radec[1], Math.min(1, 15 * view.fov / view.largestDim), view.aladin);
return; // when in TOOL_SIMBAD_POINTER mode, we do not call the listeners
} // popup to show ?
var objs = view.closestObjects(xymouse.x, xymouse.y, 5);
if (!wasDragging && objs) {
var o = objs[0]; // footprint selection code adapted from Fabrizio Giordano dev. from Serco for ESA/ESDC
if (o instanceof Footprint || o instanceof Circle) {
o.dispatchClickEvent();
} // display marker
else if (o.marker) {
// could be factorized in Source.actionClicked
view.popup.setTitle(o.popupTitle);
view.popup.setText(o.popupDesc);
view.popup.setSource(o);
view.popup.show();
} // show measurements
else {
if (view.lastClickedObject) {
view.lastClickedObject.actionOtherObjectClicked && view.lastClickedObject.actionOtherObjectClicked();
}
o.actionClicked();
}
view.lastClickedObject = o;
var objClickedFunction = view.aladin.callbacksByEventName['objectClicked'];
typeof objClickedFunction === 'function' && objClickedFunction(o);
} else {
if (view.lastClickedObject && !wasDragging) {
view.aladin.measurementTable.hide();
view.popup.hide();
if (view.lastClickedObject instanceof Footprint) {//view.lastClickedObject.deselect();
} else {
view.lastClickedObject.actionOtherObjectClicked();
}
view.lastClickedObject = null;
var _objClickedFunction = view.aladin.callbacksByEventName['objectClicked'];
typeof _objClickedFunction === 'function' && _objClickedFunction(null);
}
} // call listener of 'click' event
var onClickFunction = view.aladin.callbacksByEventName['click'];
if (typeof onClickFunction === 'function') {
var _pos = view.aladin.pix2world(xymouse.x, xymouse.y);
if (_pos !== undefined) {
onClickFunction({
ra: _pos[0],
dec: _pos[1],
x: xymouse.x,
y: xymouse.y,
isDragging: wasDragging
});
}
} // TODO : remplacer par mecanisme de listeners
// on avertit les catalogues progressifs
view.refreshProgressiveCats();
view.requestRedraw(true);
};
view.reticleCanvas.addEventListener("click", clickEvent);
view.reticleCanvas.addEventListener("mouseout", clickEvent);
view.reticleCanvas.addEventListener("touchend", clickEvent);
var lastHoveredObject; // save last object hovered by mouse
var lastMouseMovePos = null;
var mouseMoveEvent = function mouseMoveEvent(e) {
e.preventDefault();
if (e.type === 'touchmove' && view.pinchZoomParameters.isPinching && e.originalEvent && e.originalEvent.touches && e.originalEvent.touches.length === 2) {
var dist = Math.sqrt(Math.pow(e.originalEvent.touches[0].clientX - e.originalEvent.touches[1].clientX, 2) + Math.pow(e.originalEvent.touches[0].clientY - e.originalEvent.touches[1].clientY, 2));
view.setZoom(view.pinchZoomParameters.initialFov * view.pinchZoomParameters.initialDistance / dist);
return;
}
var xymouse = view.imageCanvas.relMouseCoords(e);
if (!view.dragging || hasTouchEvents) {
// update location box
updateLocation(view, xymouse.x, xymouse.y); // call listener of 'mouseMove' event
var onMouseMoveFunction = view.aladin.callbacksByEventName['mouseMove'];
if (typeof onMouseMoveFunction === 'function') {
var pos = view.aladin.pix2world(xymouse.x, xymouse.y);
if (pos !== undefined) {
onMouseMoveFunction({
ra: pos[0],
dec: pos[1],
x: xymouse.x,
y: xymouse.y
});
} // send null ra and dec when we go out of the "sky"
else if (lastMouseMovePos != null) {
onMouseMoveFunction({
ra: null,
dec: null,
x: xymouse.x,
y: xymouse.y
});
}
lastMouseMovePos = pos;
}
if (!view.dragging && !view.mode === View.SELECT) {
// objects under the mouse ?
var closest = view.closestObjects(xymouse.x, xymouse.y, 5);
if (closest) {
view.setCursor('pointer');
var objHoveredFunction = view.aladin.callbacksByEventName['objectHovered'];
if (typeof objHoveredFunction === 'function' && closest[0] !== lastHoveredObject) {
objHoveredFunction(closest[0]);
}
lastHoveredObject = closest[0];
} else {
view.setCursor('default');
var _objHoveredFunction = view.aladin.callbacksByEventName['objectHovered'];
if (typeof _objHoveredFunction === 'function' && lastHoveredObject) {
lastHoveredObject = null; // call callback function to notify we left the hovered object
_objHoveredFunction(null);
}
}
}
if (!hasTouchEvents) {
return;
}
}
if (!view.dragging) {
return;
} // var xoffset, yoffset;
var pos1, pos2;
if (e.originalEvent && e.originalEvent.targetTouches) {
// ???
// let xoffset = e.originalEvent.targetTouches[0].clientX-view.dragx;
// let yoffset = e.originalEvent.targetTouches[0].clientY-view.dragy;
var xy1 = AladinUtils.viewToXy(e.originalEvent.targetTouches[0].clientX, e.originalEvent.targetTouches[0].clientY, view.width, view.height, view.largestDim, view.zoomFactor);
var xy2 = AladinUtils.viewToXy(view.dragx, view.dragy, view.width, view.height, view.largestDim, view.zoomFactor);
pos1 = view.projection.unproject(xy1.x, xy1.y);
pos2 = view.projection.unproject(xy2.x, xy2.y);
} else {
/*
xoffset = e.clientX-view.dragx;
yoffset = e.clientY-view.dragy;
*/
// let xoffset = xymouse.x-view.dragx;
// let yoffset = xymouse.y-view.dragy;
var _xy = AladinUtils.viewToXy(xymouse.x, xymouse.y, view.width, view.height, view.largestDim, view.zoomFactor);
var _xy2 = AladinUtils.viewToXy(view.dragx, view.dragy, view.width, view.height, view.largestDim, view.zoomFactor);
pos1 = view.projection.unproject(_xy.x, _xy.y);
pos2 = view.projection.unproject(_xy2.x, _xy2.y);
} // TODO : faut il faire ce test ??
// var distSquared = xoffset*xoffset+yoffset*yoffset;
// if (distSquared<3) {
// return;
// }
if (e.originalEvent && e.originalEvent.targetTouches) {
view.dragx = e.originalEvent.targetTouches[0].clientX;
view.dragy = e.originalEvent.targetTouches[0].clientY;
} else {
view.dragx = xymouse.x;
view.dragy = xymouse.y;
/*
view.dragx = e.clientX;
view.dragy = e.clientY;
*/
}
if (view.mode === View.SELECT) {
view.requestRedraw();
return;
} //view.viewCenter.lon += xoffset*view.mouseMoveIncrement/Math.cos(view.viewCenter.lat*Math.PI/180.0);
/*
view.viewCenter.lon += xoffset*view.mouseMoveIncrement;
view.viewCenter.lat += yoffset*view.mouseMoveIncrement;
*/
view.viewCenter.lon += pos2.ra - pos1.ra;
view.viewCenter.lat += pos2.dec - pos1.dec; // can not go beyond poles
if (view.viewCenter.lat > 90) {
view.viewCenter.lat = 90;
} else if (view.viewCenter.lat < -90) {
view.viewCenter.lat = -90;
} // limit lon to [0, 360]
if (view.viewCenter.lon < 0) {
view.viewCenter.lon = 360 + view.viewCenter.lon;
} else if (view.viewCenter.lon > 360) {
view.viewCenter.lon = view.viewCenter.lon % 360;
}
view.realDragging = true;
view.requestRedraw();
}; //// endof mousemove ////
view.reticleCanvas.addEventListener("mousemove", mouseMoveEvent);
view.reticleCanvas.addEventListener("touchmove", mouseMoveEvent); // disable text selection on IE
view.aladinDiv.onselectstart = function () {
return false;
};
view.reticleCanvas.onwheel = function (event) {
event.preventDefault();
event.stopPropagation();
var level = view.zoomLevel;
var delta = event.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 (Object.prototype.hasOwnProperty.call(event, 'originalEvent')) {
delta = -event.originalEvent.deltaY;
}
if (delta > 0) {
level += 1;
} else {
level -= 1;
}
view.setZoomLevel(level);
return false;
};
};
var init = function init(view) {
// var stats = new Stats();
// stats.domElement.style.top = '50px';
// document.querySelectorAll('#aladin-statsDiv').forEach((item) => item.appendChild(stats.domElement));
// if ($('#aladin-statsDiv').length>0) {
// $('#aladin-statsDiv')[0].appendChild( stats.domElement );
// }
// view.stats = stats;
view.createListeners(view);
view.executeCallbacksThrottled = Utils.throttle(function () {
var pos = view.aladin.pix2world(view.width / 2, view.height / 2);
var fov = view.fov;
if (pos === undefined || fov === undefined) {
return;
}
var ra = pos[0];
var dec = pos[1]; // trigger callback only if position has changed !
if (ra !== this.ra || dec !== this.dec) {
var posChangedFn = view.aladin.callbacksByEventName['positionChanged'];
typeof posChangedFn === 'function' && posChangedFn({
ra: ra,
dec: dec,
dragging: true
}); // finally, save ra and dec value
this.ra = ra;
this.dec = dec;
} // trigger callback only if FoV (zoom) has changed !
if (fov !== this.old_fov) {
var fovChangedFn = view.aladin.callbacksByEventName['zoomChanged'];
typeof fovChangedFn === 'function' && fovChangedFn(fov); // finally, save fov value
this.old_fov = fov;
}
}, View.CALLBACKS_THROTTLE_TIME_MS);
view.displayHpxGrid = false;
view.displaySurvey = true;
view.displayCatalog = false;
view.displayReticle = true; // initial draw
view.fov = view.computeFov(view);
view.updateFovDiv(view);
view.redraw();
};
function updateLocation(view, x, y, isViewCenterPosition) {
if (!view.projection) {
return;
}
var xy = AladinUtils.viewToXy(x, y, view.width, view.height, view.largestDim, view.zoomFactor);
var lonlat;
try {
lonlat = view.projection.unproject(xy.x, xy.y);
} catch (err) {
console.log(err);
}
if (lonlat) {
view.location.update(lonlat.ra, lonlat.dec, view.cooFrame, isViewCenterPosition);
}
}
View.prototype.requestRedrawAtDate = function (date) {
this.dateRequestDraw = date;
};
/**
* Return the color of the lowest intensity pixel
* in teh current color map of the current background image HiPS
*/
View.prototype.getBackgroundColor = function () {
var white = 'rgb(255, 255, 255)';
var black = 'rgb(0, 0, 0)';
if (!this.imageSurvey) {
return black;
}
var cm = this.imageSurvey.getColorMap();
if (!cm) {
return black;
}
if (cm.mapName === 'native' || cm.mapName === 'grayscale') {
return cm.reversed ? white : black;
}
var idx = cm.reversed ? 255 : 0;
var r = ColorMap.MAPS[cm.mapName].r[idx];
var g = ColorMap.MAPS[cm.mapName].g[idx];
var b = ColorMap.MAPS[cm.mapName].b[idx];
return 'rgb(' + r + ',' + g + ',' + b + ')';
};
View.prototype.getViewParams = function () {
var resolution = this.width > this.height ? this.fov / this.width : this.fov / this.height;
return {
fov: [this.width * resolution, this.height * resolution],
width: this.width,
height: this.height
};
};
/**
* redraw the whole view
*/
View.prototype.redraw = function () {
var saveNeedRedraw = this.needRedraw;
window.requestAnimationFrame(this.redraw.bind(this));
var now = new Date().getTime();
if (this.dateRequestDraw && now > this.dateRequestDraw) {
this.dateRequestDraw = null;
} else if (!this.needRedraw) {
if (!this.flagForceRedraw) {
return;
} else {
this.flagForceRedraw = false;
}
} // this.stats.update();
var imageCtx = this.imageCtx; //////// 1. Draw images ////////
if (imageCtx.start2D) {
imageCtx.start2D();
} //// clear canvas ////
// TODO : do not need to clear if fov small enough ?
imageCtx.clearRect(0, 0, this.imageCanvas.width, this.imageCanvas.height); ////////////////////////
var bkgdColor = this.getBackgroundColor(); // fill with background of the same color than the first color map value (lowest intensity)
if (this.projectionMethod === ProjectionEnum.SIN) {
if (this.fov >= 60) {
imageCtx.fillStyle = bkgdColor;
imageCtx.beginPath();
var maxCxCy = this.cx > this.cy ? this.cx : this.cy;
imageCtx.arc(this.cx, this.cy, maxCxCy * this.zoomFactor, 0, 2 * Math.PI, true);
imageCtx.fill();
} // pour eviter les losanges blancs qui apparaissent quand les tuiles sont en attente de chargement
else {
imageCtx.fillStyle = bkgdColor;
imageCtx.fillRect(0, 0, this.imageCanvas.width, this.imageCanvas.height);
}
} else if (this.projectionMethod === ProjectionEnum.AITOFF) {
if (imageCtx.ellipse) {
imageCtx.fillStyle = bkgdColor;
imageCtx.beginPath();
imageCtx.ellipse(this.cx, this.cy, 2.828 * this.cx * this.zoomFactor, this.cx * this.zoomFactor * 1.414, 0, 0, 2 * Math.PI);
imageCtx.fill();
}
}
if (imageCtx.finish2D) {
imageCtx.finish2D();
}
this.projection.setCenter(this.viewCenter.lon, this.viewCenter.lat); // do we have to redo that every time? Probably not
this.projection.setProjection(this.projectionMethod); // ************* Draw allsky tiles (low resolution) *****************
// let cornersXYViewMapHighres = null;
// Pour traitement des DEFORMATIONS --> TEMPORAIRE, draw deviendra la methode utilisee systematiquement
if (this.imageSurvey && this.imageSurvey.isReady && this.displaySurvey) {
if (this.aladin.reduceDeformations == null) {
this.imageSurvey.draw(imageCtx, this, !this.dragging, this.curNorder);
} else {
this.imageSurvey.draw(imageCtx, this, this.aladin.reduceDeformations, this.curNorder);
}
}
/*
else {
var cornersXYViewMapAllsky = this.getVisibleCells(3);
var cornersXYViewMapHighres = null;
if (this.curNorder>=3) {
if (this.curNorder==3) {
cornersXYViewMapHighres = cornersXYViewMapAllsky;
}
else {
cornersXYViewMapHighres = this.getVisibleCells(this.curNorder);
}
}
// redraw image survey
if (this.imageSurvey && this.imageSurvey.isReady && this.displaySurvey) {
// TODO : a t on besoin de dessiner le allsky si norder>=3 ?
// TODO refactoring : should be a method of HpxImageSurvey
this.imageSurvey.redrawAllsky(imageCtx, cornersXYViewMapAllsky, this.fov, this.curNorder);
if (this.curNorder>=3) {
this.imageSurvey.redrawHighres(imageCtx, cornersXYViewMapHighres, this.curNorder);
}
}
}
*/
// redraw overlay image survey
// TODO : does not work if different frames
// TODO: use HpxImageSurvey.draw method !!
if (this.overlayImageSurvey && this.overlayImageSurvey.isReady) {
imageCtx.globalAlpha = this.overlayImageSurvey.getAlpha();
if (this.aladin.reduceDeformations == null) {
this.overlayImageSurvey.draw(imageCtx, this, !this.dragging, this.curOverlayNorder);
} else {
this.overlayImageSurvey.draw(imageCtx, this, this.aladin.reduceDeformations, this.curOverlayNorder);
}
/*
if (this.fov>50) {
this.overlayImageSurvey.redrawAllsky(imageCtx, cornersXYViewMapAllsky, this.fov, this.curOverlayNorder);
}
if (this.curOverlayNorder>=3) {
var norderOverlay = Math.min(this.curOverlayNorder, this.overlayImageSurvey.maxOrder);
if ( cornersXYViewMapHighres==null || norderOverlay != this.curNorder ) {
cornersXYViewMapHighres = this.getVisibleCells(norderOverlay);
}
this.overlayImageSurvey.redrawHighres(imageCtx, cornersXYViewMapHighres, norderOverlay);
}
*/
imageCtx.globalAlpha = 1.0;
} // redraw HEALPix grid
if (this.displayHpxGrid) {
var cornersXYViewMapAllsky = this.getVisibleCells(3);
var cornersXYViewMapHighres = null;
if (this.curNorder >= 3) {
if (this.curNorder === 3) {
cornersXYViewMapHighres = cornersXYViewMapAllsky;
} else {
cornersXYViewMapHighres = this.getVisibleCells(this.curNorder);
}
}
if (cornersXYViewMapHighres && this.curNorder > 3) {
this.healpixGrid.redraw(imageCtx, cornersXYViewMapHighres, this.fov, this.curNorder);
} else {
this.healpixGrid.redraw(imageCtx, cornersXYViewMapAllsky, this.fov, 3);
}
} // redraw coordinates grid
if (this.showGrid) {
if (this.cooGrid == null) {
this.cooGrid = new CooGrid();
}
this.cooGrid.redraw(imageCtx, this.projection, this.cooFrame, this.width, this.height, this.largestDim, this.zoomFactor, this.fov);
} ////// 2. Draw catalogues////////
var catalogCtx = this.catalogCtx;
var catalogCanvasCleared = false;
if (this.mustClearCatalog) {
catalogCtx.clearRect(0, 0, this.width, this.height);
catalogCanvasCleared = true;
this.mustClearCatalog = false;
}
if (this.catalogs && this.catalogs.length > 0 && this.displayCatalog && (!this.dragging || View.DRAW_SOURCES_WHILE_DRAGGING)) {
// TODO : do not clear every time
//// clear canvas ////
if (!catalogCanvasCleared) {
catalogCtx.clearRect(0, 0, this.width, this.height);
catalogCanvasCleared = true;
}
for (var i = 0; i < this.catalogs.length; i++) {
var cat = this.catalogs[i];
cat.draw(catalogCtx, this.projection, this.cooFrame, this.width, this.height, this.largestDim, this.zoomFactor);
}
} // draw popup catalog
if (this.catalogForPopup.isShowing && this.catalogForPopup.sources.length > 0) {
if (!catalogCanvasCleared) {
catalogCtx.clearRect(0, 0, this.width, this.height);
catalogCanvasCleared = true;
}
this.catalogForPopup.draw(catalogCtx, this.projection, this.cooFrame, this.width, this.height, this.largestDim, this.zoomFactor);
} ////// 3. Draw overlays////////
var overlayCtx = this.catalogCtx;
if (this.overlays && this.overlays.length > 0 && (!this.dragging || View.DRAW_SOURCES_WHILE_DRAGGING)) {
if (!catalogCanvasCleared) {
catalogCtx.clearRect(0, 0, this.width, this.height);
catalogCanvasCleared = true;
}
for (var _i = 0; _i < this.overlays.length; _i++) {
this.overlays[_i].draw(overlayCtx, this.projection, this.cooFrame, this.width, this.height, this.largestDim, this.zoomFactor);
}
} // draw MOCs
var mocCtx = this.catalogCtx;
if (this.mocs && this.mocs.length > 0 && (!this.dragging || View.DRAW_MOCS_WHILE_DRAGGING)) {
if (!catalogCanvasCleared) {
catalogCtx.clearRect(0, 0, this.width, this.height);
catalogCanvasCleared = true;
}
for (var _i2 = 0; _i2 < this.mocs.length; _i2++) {
this.mocs[_i2].draw(mocCtx, this.projection, this.cooFrame, this.width, this.height, this.largestDim, this.zoomFactor, this.fov);
}
} // if (this.mode===View.SELECT) {
// var mustRedrawReticle = true;
// }
////// 4. Draw reticle ///////
// TODO: reticle should be placed in a static DIV, no need to waste a canvas
var reticleCtx = this.reticleCtx;
if (this.mustRedrawReticle || this.mode === View.SELECT) {
reticleCtx.clearRect(0, 0, this.width, this.height);
}
if (this.displayReticle) {
if (!this.reticleCache) {
// build reticle image
var c = document.createElement('canvas');
var s = this.options.reticleSize;
c.width = s;
c.height = s;
var ctx = c.getContext('2d');
ctx.lineWidth = 2;
ctx.strokeStyle = this.options.reticleColor;
ctx.beginPath();
ctx.moveTo(s / 2, s / 2 + (s / 2 - 1));
ctx.lineTo(s / 2, s / 2 + 2);
ctx.moveTo(s / 2, s / 2 - (s / 2 - 1));
ctx.lineTo(s / 2, s / 2 - 2);
ctx.moveTo(s / 2 + (s / 2 - 1), s / 2);
ctx.lineTo(s / 2 + 2, s / 2);
ctx.moveTo(s / 2 - (s / 2 - 1), s / 2);
ctx.lineTo(s / 2 - 2, s / 2);
ctx.stroke();
this.reticleCache = c;
}
reticleCtx.drawImage(this.reticleCache, this.width / 2 - this.reticleCache.width / 2, this.height / 2 - this.reticleCache.height / 2);
this.mustRedrawReticle = false;
} ////// 5. Draw all-sky ring /////
if (this.projectionMethod === ProjectionEnum.SIN && this.fov >= 60 && this.aladin.options['showAllskyRing'] === true) {
imageCtx.strokeStyle = this.aladin.options['allskyRingColor'];
var ringWidth = this.aladin.options['allskyRingWidth'];
imageCtx.lineWidth = ringWidth;
imageCtx.beginPath();
var _maxCxCy = this.cx > this.cy ? this.cx : this.cy;
imageCtx.arc(this.cx, this.cy, (_maxCxCy - ringWidth / 2.0 + 1) * this.zoomFactor, 0, 2 * Math.PI, true);
imageCtx.stroke();
} // draw selection box
if (this.mode === View.SELECT && this.dragging) {
reticleCtx.fillStyle = "rgba(100, 240, 110, 0.25)";
var w = this.dragx - this.selectStartCoo.x;
var h = this.dragy - this.selectStartCoo.y;
reticleCtx.fillRect(this.selectStartCoo.x, this.selectStartCoo.y, w, h);
} // TODO : is this the right way?
if (saveNeedRedraw === this.needRedraw) {
this.needRedraw = false;
} // objects lookup
if (!this.dragging) {
this.updateObjectsLookup();
} // execute 'positionChanged' and 'zoomChanged' callbacks
this.executeCallbacksThrottled();
};
View.prototype.forceRedraw = function () {
this.flagForceRedraw = true;
};
View.prototype.refreshProgressiveCats = function () {
if (!this.catalogs) {
return;
}
for (var i = 0; i < this.catalogs.length; i++) {
if (this.catalogs[i].type === 'progressivecat') {
this.catalogs[i].loadNeededTiles();
}
}
};
View.prototype.getVisiblePixList = function (norder, frameSurvey) {
var nside = Math.pow(2, norder);
var pixList;
var npix = HealpixIndex.nside2Npix(nside);
if (this.fov > 80) {
pixList = [];
for (var ipix = 0; ipix < npix; ipix++) {
pixList.push(ipix);
}
} else {
var hpxIdx = new HealpixIndex(nside);
hpxIdx.init();
var spatialVector = new SpatialVector(); // if frame != frame image survey, we need to convert to survey frame system
var xy = AladinUtils.viewToXy(this.cx, this.cy, this.width, this.height, this.largestDim, this.zoomFactor);
var radec = this.projection.unproject(xy.x, xy.y);
var lonlat = [];
if (frameSurvey && frameSurvey.system !== this.cooFrame.system) {
if (frameSurvey.system === CooFrameEnum.SYSTEMS.J2000) {
lonlat = CooConversion.GalacticToJ2000([radec.ra, radec.dec]);
} else if (frameSurvey.system === CooFrameEnum.SYSTEMS.GAL) {
lonlat = CooConversion.J2000ToGalactic([radec.ra, radec.dec]);
}
} else {
lonlat = [radec.ra, radec.dec];
}
if (this.imageSurvey && this.imageSurvey.longitudeReversed === true) {
spatialVector.set(lonlat[0], lonlat[1]);
} else {
spatialVector.set(lonlat[0], lonlat[1]);
}
var radius = this.fov * 0.5 * this.ratio; // we need to extend the radius
if (this.fov > 60) {
radius *= 1.6;
} else if (this.fov > 12) {
radius *= 1.45;
} else {
radius *= 1.1;
}
pixList = hpxIdx.queryDisc(spatialVector, radius * Math.PI / 180.0, true, true); // add central pixel at index 0
var polar = Utils.radecToPolar(lonlat[0], lonlat[1]);
var ipixCenter = hpxIdx.ang2pix_nest(polar.theta, polar.phi);
pixList.unshift(ipixCenter);
}
return pixList;
}; // TODO: optimize this method !!
View.prototype.getVisibleCells = function (norder, frameSurvey) {
if (!frameSurvey && this.imageSurvey) {
frameSurvey = this.imageSurvey.cooFrame;
}
var cells = []; // array to be returned
var cornersXY = [];
var spVec = new SpatialVector();
var nside = Math.pow(2, norder); // TODO : to be modified
var npix = HealpixIndex.nside2Npix(nside);
var ipixCenter = null; // build list of pixels
// TODO: pixList can be obtained from getVisiblePixList
var pixList;
if (this.fov > 80) {
pixList = [];
for (var _ipix = 0; _ipix < npix; _ipix++) {
pixList.push(_ipix);
}
} else {
var hpxIdx = new HealpixIndex(nside);
var spatialVector = new SpatialVector(); // if frame != frame image survey, we need to convert to survey frame system
var xy = AladinUtils.viewToXy(this.cx, this.cy, this.width, this.height, this.largestDim, this.zoomFactor);
var radec = this.projection.unproject(xy.x, xy.y);
var lonlat = [];
if (frameSurvey && frameSurvey.system !== this.cooFrame.system) {
if (frameSurvey.system === CooFrameEnum.SYSTEMS.J2000) {
lonlat = CooConversion.GalacticToJ2000([radec.ra, radec.dec]);
} else if (frameSurvey.system === CooFrameEnum.SYSTEMS.GAL) {
lonlat = CooConversion.J2000ToGalactic([radec.ra, radec.dec]);
}
} else {
lonlat = [radec.ra, radec.dec];
}
if (this.imageSurvey && this.imageSurvey.longitudeReversed === true) {
spatialVector.set(lonlat[0], lonlat[1]);
} else {
spatialVector.set(lonlat[0], lonlat[1]);
}
var radius = this.fov * 0.5 * this.ratio; // we need to extend the radius
if (this.fov > 60) {
radius *= 1.6;
} else if (this.fov > 12) {
radius *= 1.45;
} else {
radius *= 1.1;
}
pixList = hpxIdx.queryDisc(spatialVector, radius * Math.PI / 180.0, true, true); // add central pixel at index 0
var polar = Utils.radecToPolar(lonlat[0], lonlat[1]);
ipixCenter = hpxIdx.ang2pix_nest(polar.theta, polar.phi);
pixList.unshift(ipixCenter);
}
var ipix;
var lon, lat;
for (var ipixIdx = 0, len = pixList.length; ipixIdx < len; ipixIdx++) {
ipix = pixList[ipixIdx];
if (ipix === ipixCenter && ipixIdx > 0) {
continue;
}
var cornersXYView = [];
var corners = HealpixCache.corners_nest(ipix, nside);
for (var k = 0; k < 4; k++) {
if (corners) spVec.setXYZ(corners[k].x, corners[k].y, corners[k].z); // need for frame transformation ?
if (frameSurvey && frameSurvey.system !== this.cooFrame.system) {
if (frameSurvey.system === CooFrameEnum.SYSTEMS.J2000) {
var _radec = CooConversion.J2000ToGalactic([spVec.ra(), spVec.dec()]);
lon = _radec[0];
lat = _radec[1];
} else if (frameSurvey.system === CooFrameEnum.SYSTEMS.GAL) {
var _radec2 = CooConversion.GalacticToJ2000([spVec.ra(), spVec.dec()]);
lon = _radec2[0];
lat = _radec2[1];
}
} else {
lon = spVec.ra();
lat = spVec.dec();
}
cornersXY[k] = this.projection.project(lon, lat);
}
if (cornersXY[0] == null || cornersXY[1] == null || cornersXY[2] == null || cornersXY[3] == null) {
continue;
}
for (var _k = 0; _k < 4; _k++) {
cornersXYView[_k] = AladinUtils.xyToView(cornersXY[_k].X, cornersXY[_k].Y, this.width, this.height, this.largestDim, this.zoomFactor);
} // var indulge = 10;
// detect pixels outside view. Could be improved !
// we minimize here the number of cells returned
if (cornersXYView[0].vx < 0 && cornersXYView[1].vx < 0 && cornersXYView[2].vx < 0 && cornersXYView[3].vx < 0) {
continue;
}
if (cornersXYView[0].vy < 0 && cornersXYView[1].vy < 0 && cornersXYView[2].vy < 0 && cornersXYView[3].vy < 0) {
continue;
}
if (cornersXYView[0].vx >= this.width && cornersXYView[1].vx >= this.width && cornersXYView[2].vx >= this.width && cornersXYView[3].vx >= this.width) {
continue;
}
if (cornersXYView[0].vy >= this.height && cornersXYView[1].vy >= this.height && cornersXYView[2].vy >= this.height && cornersXYView[3].vy >= this.height) {
continue;
} // check if pixel is visible
// if (this.fov<160) { // don't bother checking if fov is large enough
// if ( ! AladinUtils.isHpxPixVisible(cornersXYView, this.width, this.height) ) {
// continue;
// }
// }
// check if we have a pixel at the edge of the view in AITOFF --> TO BE MODIFIED
if (this.projection.PROJECTION === ProjectionEnum.AITOFF) {
var xdiff = cornersXYView[0].vx - cornersXYView[2].vx;
var ydiff = cornersXYView[0].vy - cornersXYView[2].vy;
var distDiag = Math.sqrt(xdiff * xdiff + ydiff * ydiff);
if (distDiag > this.largestDim / 5) {
continue;
}
xdiff = cornersXYView[1].vx - cornersXYView[3].vx;
ydiff = cornersXYView[1].vy - cornersXYView[3].vy;
distDiag = Math.sqrt(xdiff * xdiff + ydiff * ydiff);
if (distDiag > this.largestDim / 5) {
continue;
}
}
cornersXYView.ipix = ipix;
cells.push(cornersXYView);
}
return cells;
}; // get position in view for a given HEALPix cell
View.prototype.getPositionsInView = function (ipix, norder) {
var cornersXY = [];
var lon, lat;
var spVec = new SpatialVector();
var nside = Math.pow(2, norder); // TODO : to be modified
var cornersXYView = []; // will be returned
var corners = HealpixCache.corners_nest(ipix, nside);
for (var k = 0; k < 4; k++) {
spVec.setXYZ(corners[k].x, corners[k].y, corners[k].z); // need for frame transformation ?
if (this.imageSurvey && this.imageSurvey.cooFrame.system !== this.cooFrame.system) {
if (this.imageSurvey.cooFrame.system === CooFrameEnum.SYSTEMS.J2000) {
var radec = CooConversion.J2000ToGalactic([spVec.ra(), spVec.dec()]);
lon = radec[0];
lat = radec[1];
} else if (this.imageSurvey.cooFrame.system === CooFrameEnum.SYSTEMS.GAL) {
var _radec3 = CooConversion.GalacticToJ2000([spVec.ra(), spVec.dec()]);
lon = _radec3[0];
lat = _radec3[1];
}
} else {
lon = spVec.ra();
lat = spVec.dec();
}
cornersXY[k] = this.projection.project(lon, lat);
}
if (cornersXY[0] == null || cornersXY[1] == null || cornersXY[2] == null || cornersXY[3] == null) {
return null;
}
for (var _k2 = 0; _k2 < 4; _k2++) {
cornersXYView[_k2] = AladinUtils.xyToView(cornersXY[_k2].X, cornersXY[_k2].Y, this.width, this.height, this.largestDim, this.zoomFactor);
}
return cornersXYView;
};
View.prototype.computeZoomFactor = function (level) {
if (level > 0) {
return AladinUtils.getZoomFactorForAngle(180 / Math.pow(1.15, level), this.projectionMethod);
} else {
return 1 + 0.1 * level;
}
};
View.prototype.setZoom = function (fovDegrees) {
if (fovDegrees < 0 ||