@cquiroz/aladin-lite
Version:
AladinLite module
621 lines (524 loc) • 20.4 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 ProgressiveCat.js
*
* Author: Thomas Boch[CDS]
*
*****************************************************************************/
import Utils from './Utils';
import CooFrameEnum from './CooFrameEnum';
import Color from './Color';
import Source from './Source';
import Catalog from './Catalog';
import Coo from './coo'; // import $ from 'jquery';
// TODO: index sources according to their HEALPix ipix
// TODO : merge parsing with class Catalog
var ProgressiveCat = function () {
// TODO : test if CORS support. If no, need to pass through a proxy
// currently, we suppose CORS is supported
// constructor
var ProgressiveCat = function ProgressiveCat(rootUrl, frameStr, maxOrder, options) {
options = options || {};
this.type = 'progressivecat';
this.rootUrl = rootUrl; // TODO: method to sanitize rootURL (absolute, no duplicate slashes, remove end slash if existing)
// fast fix for HTTPS support --> will work for all HiPS served by CDS
if (Utils.isHttpsContext() && (/u-strasbg.fr/i.test(this.rootUrl) || /unistra.fr/i.test(this.rootUrl))) {
this.rootUrl = this.rootUrl.replace('http://', 'https://');
}
this.frameStr = frameStr;
this.frame = CooFrameEnum.fromString(frameStr) || CooFrameEnum.J2000;
this.maxOrder = maxOrder;
this.isShowing = true; // TODO : inherit from catalogue
this.name = options.name || "progressive-cat";
this.color = options.color || Color.getNextColor();
this.shape = options.shape || "square";
this.sourceSize = options.sourceSize || 6;
this.selectSize = this.sourceSize + 2; // allows for filtering of sources
this.filterFn = options.filter || undefined; // TODO: do the same for catalog
this.selectionColor = '#00ff00'; // TODO: to be merged with Catalog
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(100);
this.updateShape(options);
this.maxOrderAllsky = 2;
this.isReady = false;
}; // TODO: to be put higher in the class diagram, in a HiPS generic class
ProgressiveCat.readProperties = function (rootUrl, successCallback, errorCallback) {
if (!successCallback) {
return;
}
var propertiesURL = rootUrl + '/properties'; // $.ajax({
// url: propertiesURL,
// method: 'GET',
// dataType: 'text',
// success: function(propertiesTxt) {
// var props = {};
// var lines = propertiesTxt.split('\n');
// for (var k=0; k<lines.length; k++) {
// var line = lines[k];
// var idx = line.indexOf('=');
// var propName = $.trim(line.substring(0, idx));
// var propValue = $.trim(line.substring(idx + 1));
// props[propName] = propValue;
// }
// successCallback(props);
// },
// error: function(err) { // TODO : which parameters should we put in the error callback
// errorCallback && errorCallback(err);
// }
// });
fetch(propertiesURL).then(res => {
var type = res.headers.get('content-type');
if (type.includes("application/json")) return res.json();else if (type.includes("text/html")) return res.text();
}).then(data => {
var props = {};
var lines = data.split('\n');
for (var k = 0; k < lines.length; k++) {
var line = lines[k];
var idx = line.indexOf('=');
var propName = line.substring(0, idx).trim();
var propValue = line.substring(idx + 1).trim();
props[propName] = propValue;
}
successCallback(props);
}).catch(err => errorCallback && errorCallback(err));
};
function getFields(instance, xml) {
var attributes = ["name", "ID", "ucd", "utype", "unit", "datatype", "arraysize", "width", "precision"];
var fields = [];
instance.keyRa = instance.keyDec = null; // var k = 0;
// $(xml).find("FIELD").each(function() {
// var f = {};
// for (var i=0; i<attributes.length; i++) {
// var attribute = attributes[i];
// if ($(this).attr(attribute)) {
// f[attribute] = $(this).attr(attribute);
// }
// }
// if ( ! f.ID) {
// f.ID = "col_" + k;
// }
// if (!instance.keyRa && f.ucd && (f.ucd.indexOf('pos.eq.ra')===0 || f.ucd.indexOf('POS_EQ_RA')===0)) {
// if (f.name) {
// instance.keyRa = f.name;
// }
// else {
// instance.keyRa = f.ID;
// }
// }
// if (!instance.keyDec && f.ucd && (f.ucd.indexOf('pos.eq.dec')===0 || f.ucd.indexOf('POS_EQ_DEC')===0)) {
// if (f.name) {
// instance.keyDec = f.name;
// }
// else {
// instance.keyDec = f.ID;
// }
// }
// fields.push(f);
// k++;
// });
var parser = new DOMParser();
var xmlDoc = parser.parseFromString(xml, "text/xml");
var xmlFields = xmlDoc.getElementsByTagName("FIELD");
for (var j = 0; j < xmlFields.length; j++) {
var f = {};
for (var i = 0; i < attributes.length; i++) {
var attribute = attributes[i];
if (xmlFields[j].hasAttribute(attribute)) {
f[attribute] = xmlFields[j].getAttribute(attribute);
}
}
if (!f.ID) {
f.ID = "col_" + j;
}
if (!instance.keyRa && f.ucd && (f.ucd.indexOf('pos.eq.ra') === 0 || f.ucd.indexOf('POS_EQ_RA') === 0)) {
if (f.name) instance.keyRa = f.name;else instance.keyRa = f.ID;
}
if (!instance.keyDec && f.ucd && (f.ucd.indexOf('pos.eq.dec') === 0 || f.ucd.indexOf('POS_EQ_DEC') === 0)) {
if (f.name) instance.keyDec = f.name;else instance.keyDec = f.ID;
}
fields.push(f);
}
return fields;
}
function getSources(instance, csv, fields) {
// TODO : find ra and dec key names (see in Catalog)
if (!instance.keyRa || !instance.keyDec) {
return [];
}
var lines = csv.split('\n');
var mesureKeys = [];
for (var k = 0; k < fields.length; k++) {
if (fields[k].name) {
mesureKeys.push(fields[k].name);
} else {
mesureKeys.push(fields[k].ID);
}
}
var sources = [];
var coo = new Coo();
var newSource; // start at i=1, as first line repeat the fields names
for (var i = 2; i < lines.length; i++) {
var mesures = {};
var data = lines[i].split('\t');
if (data.length < mesureKeys.length) {
continue;
}
for (var j = 0; j < mesureKeys.length; j++) {
mesures[mesureKeys[j]] = data[j];
}
var ra, dec;
if (Utils.isNumber(mesures[instance.keyRa]) && Utils.isNumber(mesures[instance.keyDec])) {
ra = parseFloat(mesures[instance.keyRa]);
dec = parseFloat(mesures[instance.keyDec]);
} else {
coo.parse(mesures[instance.keyRa] + " " + mesures[instance.keyDec]);
ra = coo.lon;
dec = coo.lat;
}
newSource = new Source(ra, dec, mesures);
sources.push(newSource);
newSource.setCatalog(instance);
}
return sources;
} //ProgressiveCat.prototype.updateShape = cds.Catalog.prototype.updateShape;
ProgressiveCat.prototype = {
init: function init(view) {
var self = this;
this.view = view;
if (this.maxOrder && this.frameStr) {
this._loadMetadata();
} else {
ProgressiveCat.readProperties(self.rootUrl, function (properties) {
self.properties = properties;
self.maxOrder = self.properties['hips_order'];
self.frame = CooFrameEnum.fromString(self.properties['hips_frame']);
self._loadMetadata();
}, function () {
console.log('Could not find properties for HiPS ' + self.rootUrl);
});
}
},
// updateShape: cds.Catalog.prototype.updateShape,
_loadMetadata: function _loadMetadata() {
var self = this; // $.ajax({
// url: self.rootUrl + '/Metadata.xml',
// method: 'GET',
// success: function(xml) {
// self.fields = getFields(self, xml);
// self._loadAllskyNewMethod();
// },
// error: function() {
// self._loadAllskyOldMethod();
// }
// });
fetch("".concat(self.rootUrl, "/Metadata.xml")).then(res => res.text()).then(data => {
self.fields = getFields(self, data);
self._loadAllskyNewMethod();
}).catch(() => {
self._loadAllskyOldMethod();
});
},
_loadAllskyNewMethod: function _loadAllskyNewMethod() {
var self = this; // $.ajax({
// url: self.rootUrl + '/Norder1/Allsky.tsv',
// method: 'GET',
// success: function(tsv) {
// self.order1Sources = getSources(self, tsv, self.fields);
// if (self.order2Sources) {
// self.isReady = true;
// self._finishInitWhenReady();
// }
// },
// error: function(err) {
// console.log('Something went wrong: ' + err);
// }
// });
fetch("".concat(self.rootUrl, "/Norder1/Allsky.tsv")).then(res => res.text()).then(data => {
self.order1Sources = getSources(self, data, self.fields);
if (self.order2Sources) {
self.isReady = true;
self._finishInitWhenReady();
}
}).catch(err => console.log("Something went wrong: ".concat(err))); // $.ajax({
// url: self.rootUrl + '/Norder2/Allsky.tsv',
// method: 'GET',
// success: function(tsv) {
// self.order2Sources = getSources(self, tsv, self.fields);
// if (self.order1Sources) {
// self.isReady = true;
// self._finishInitWhenReady();
// }
// },
// error: function(err) {
// console.log('Something went wrong: ' + err);
// }
// });
fetch("".concat(self.rootUrl, "/Norder2/Allsky.tsv")).then(res => res.text()).then(data => {
self.order2Sources = getSources(self, data, self.fields);
if (self.order1Sources) {
self.isReady = true;
self._finishInitWhenReady();
}
}).catch(err => console.log("Something went wrong: ".concat(err)));
},
_loadAllskyOldMethod: function _loadAllskyOldMethod() {
this.maxOrderAllsky = 3;
this._loadLevel2Sources();
this._loadLevel3Sources();
},
_loadLevel2Sources: function _loadLevel2Sources() {
var self = this; // $.ajax({
// url: self.rootUrl + '/Norder2/Allsky.xml',
// method: 'GET',
// success: function(xml) {
// self.fields = getFields(self, xml);
// self.order2Sources = getSources(self, $(xml).find('CSV').text(), self.fields);
// if (self.order3Sources) {
// self.isReady = true;
// self._finishInitWhenReady();
// }
// },
// error: function(err) {
// console.log('Something went wrong: ' + err);
// }
// });
fetch("".concat(self.rootUrl, "/Norder2/Allsky.xml")).then(res => res.text()).then(data => {
self.fields = getFields(self, data);
var parser = new DOMParser();
var xmlDoc = parser.parseFromString(data, "text/xml");
self.order2Sources = getSources(self, xmlDoc.getElementsByTagName("CSV")[0].innerHTML, self.fields);
if (self.order3Sources) {
self.isReady = true;
self._finishInitWhenReady();
}
}).catch(err => console.log("Something went wrong: ".concat(err)));
},
_loadLevel3Sources: function _loadLevel3Sources() {
var self = this; // $.ajax({
// url: self.rootUrl + '/Norder3/Allsky.xml',
// method: 'GET',
// success: function(xml) {
// self.order3Sources = getSources(self, $(xml).find('CSV').text(), self.fields);
// if (self.order2Sources) {
// self.isReady = true;
// self._finishInitWhenReady();
// }
// },
// error: function(err) {
// console.log('Something went wrong: ' + err);
// }
// });
fetch("".concat(self.rootUrl, "/Norder3/Allsky.xml")).then(res => res.text()).then(data => {
var parser = new DOMParser();
var xmlDoc = parser.parseFromString(data, "text/xml");
self.order3Sources = getSources(self, xmlDoc.getElementsByTagName("CSV")[0].innerHTML, self.fields);
if (self.order2Sources) {
self.isReady = true;
self._finishInitWhenReady();
}
}).catch(err => console.log("Something went wrong: ".concat(err)));
},
_finishInitWhenReady: function _finishInitWhenReady() {
this.view.requestRedraw();
this.loadNeededTiles();
},
draw: function draw(ctx, projection, frame, width, height, largestDim, zoomFactor) {
if (!this.isShowing || !this.isReady) {
return;
}
this.drawSources(this.order1Sources, ctx, projection, frame, width, height, largestDim, zoomFactor);
this.drawSources(this.order2Sources, ctx, projection, frame, width, height, largestDim, zoomFactor);
this.drawSources(this.order3Sources, ctx, projection, frame, width, height, largestDim, zoomFactor);
if (!this.tilesInView) {
return;
}
var sources, key, t;
for (var k = 0; k < this.tilesInView.length; k++) {
t = this.tilesInView[k];
key = t[0] + '-' + t[1];
sources = this.sourcesCache.get(key);
if (sources) {
this.drawSources(sources, ctx, projection, frame, width, height, largestDim, zoomFactor);
}
}
},
drawSources: function drawSources(sources, ctx, projection, frame, width, height, largestDim, zoomFactor) {
if (!sources) {
return;
}
var s;
for (var k = 0, len = sources.length; k < len; k++) {
s = sources[k];
if (!this.filterFn || this.filterFn(s)) {
Catalog.drawSource(this, s, ctx, projection, frame, width, height, largestDim, zoomFactor);
}
}
for (var _k = 0, _len = sources.length; _k < _len; _k++) {
s = sources[_k];
if (!s.isSelected) {
continue;
}
if (!this.filterFn || this.filterFn(s)) {
Catalog.drawSourceSelection(this, s, ctx);
}
}
},
getSources: function getSources() {
var ret = [];
if (this.order1Sources) {
ret = ret.concat(this.order1Sources);
}
if (this.order2Sources) {
ret = ret.concat(this.order2Sources);
}
if (this.order3Sources) {
ret = ret.concat(this.order3Sources);
}
if (this.tilesInView) {
var sources, key, t;
for (var k = 0; k < this.tilesInView.length; k++) {
t = this.tilesInView[k];
key = t[0] + '-' + t[1];
sources = this.sourcesCache.get(key);
if (sources) {
ret = ret.concat(sources);
}
}
}
return ret;
},
deselectAll: function deselectAll() {
if (this.order1Sources) {
for (var k = 0; k < this.order1Sources.length; k++) {
this.order1Sources[k].deselect();
}
}
if (this.order2Sources) {
for (var _k2 = 0; _k2 < this.order2Sources.length; _k2++) {
this.order2Sources[_k2].deselect();
}
}
if (this.order3Sources) {
for (var _k3 = 0; _k3 < this.order3Sources.length; _k3++) {
this.order3Sources[_k3].deselect();
}
}
var keys = this.sourcesCache.keys();
for (var key in keys) {
if (!this.sourcesCache[key]) {
continue;
}
var sources = this.sourcesCache[key];
for (var _k4 = 0; _k4 < sources.length; _k4++) {
sources[_k4].deselect();
}
}
},
show: function show() {
if (this.isShowing) {
return;
}
this.isShowing = true;
this.loadNeededTiles();
this.reportChange();
},
hide: function hide() {
if (!this.isShowing) {
return;
}
this.isShowing = false;
this.reportChange();
},
getTileURL: function getTileURL(norder, npix) {
var dirIdx = Math.floor(npix / 10000) * 10000;
return this.rootUrl + "/Norder" + norder + "/Dir" + dirIdx + "/Npix" + npix + ".tsv";
},
loadNeededTiles: function loadNeededTiles() {
if (!this.isShowing) {
return;
}
this.tilesInView = [];
var norder = this.view.realNorder;
if (norder > this.maxOrder) {
norder = this.maxOrder;
}
if (norder <= this.maxOrderAllsky) {
return; // nothing to do, hurrayh !
}
var cells = this.view.getVisibleCells(norder, this.frame);
var ipixList, ipix;
for (var curOrder = 3; curOrder <= norder; curOrder++) {
ipixList = [];
for (var k = 0; k < cells.length; k++) {
ipix = Math.floor(cells[k].ipix / Math.pow(4, norder - curOrder));
if (ipixList.indexOf(ipix) < 0) {
ipixList.push(ipix);
}
} // load needed tiles
for (var i = 0; i < ipixList.length; i++) {
this.tilesInView.push([curOrder, ipixList[i]]);
}
}
var t, key;
for (var _k5 = 0; _k5 < this.tilesInView.length; _k5++) {
t = this.tilesInView[_k5];
key = t[0] + '-' + t[1]; // t[0] is norder, t[1] is ipix
if (!this.sourcesCache.get(key)) {
(function (self, norder, ipix) {
// wrapping function is needed to be able to retrieve norder and ipix in ajax success function
var key = norder + '-' + ipix; // $.ajax({
// /*
// 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
// url: self.getTileURL(norder, ipix),
// method: 'GET',
// //dataType: 'jsonp',
// success: function(tsv) {
// 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, []);
// }
// });
fetch("".concat(self.getTileURL(norder, ipix))).then(res => res.text()).then(data => {
self.sourcesCache.set(key, getSources(self, data, self.fields));
self.view.requestRedraw();
}).catch(() => {
self.sourcesCache.set(key, []);
});
})(this, t[0], t[1]);
}
}
},
reportChange: function reportChange() {
// TODO: to be shared with Catalog
this.view && this.view.requestRedraw();
}
}; // END OF .prototype functions
return ProgressiveCat;
}();
export default ProgressiveCat;