obscore fields reconize

This commit is contained in:
Matthieu BAUMANN
2023-04-03 13:33:32 +02:00
parent d520de061f
commit 7efb4e786e
14 changed files with 605 additions and 258 deletions

212
examples/ObsCore_003.xml Normal file
View File

@@ -0,0 +1,212 @@
<?xml version="1.0"?>
<VOTABLE xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.3"
xmlns="http://www.ivoa.net/xml/VOTable/v1.1"
xsi:schemaLocation="http://www.ivoa.net/xml/VOTable/v1.1 http://www.ivoa.net/xml/VOTable/v1.1">
<DESCRIPTION>Mockup SKA discovery service response VOTable</DESCRIPTION>
<COOSYS ID="J2000" equinox="2000." epoch="2000" system="eq_FK5" />
<RESOURCE name="ska.srcnet.org.resp" type="results">
<DESCRIPTION>ska.srcnet.resp/sia~1 SKA data discovery service from Italian node</DESCRIPTION>
<TABLE name="ska.scrnet.org/italy/galacticCenter">
<FIELD ID="calib_level" name="calib_level" ucd="meta.code;obs.calib" utype="obscore:ObsDataset.calibLevel"
datatype="int">
<DESCRIPTION>calibration level (0,1,2,3)</DESCRIPTION>
</FIELD>
<FIELD ID="s_ra" name="s_ra" unit="deg" ucd="pos.eq.ra"
utype="obscore:Char.SpatialAxis.Coverage.Location.Coord.Position2D.Value2.C1" datatype="double">
<DESCRIPTION>RA of central coordinates</DESCRIPTION>
</FIELD>
<FIELD ID="s_dec" name="s_dec" unit="deg" ucd="pos.eq.dec"
utype="obscore:Char.SpatialAxis.Coverage.Location.Coord.Position2D.Value2.C2" datatype="double">
<DESCRIPTION>DEC of central coordinates</DESCRIPTION>
</FIELD>
<FIELD ID="s_fov" name="s_fov" unit="deg" ucd="phys.angSize;instr.fov"
utype="obscore:Char.SpatialAxis.Coverage.Bounds.Extent.diameter" datatype="double">
<DESCRIPTION>size of the region covered (~diameter of minimum bounding circle)</DESCRIPTION>
</FIELD>
<FIELD ID="s_region" name="s_region" ucd="pos.outline;obs.field"
utype="obscore:Char.SpatialAxis.Coverage.Support.Area" datatype="char" arraysize="*">
<DESCRIPTION>region bounded by observation</DESCRIPTION>
</FIELD>
<FIELD ID="obs_publisher_did" name="obs_publisher_did" ucd="meta.ref.ivoid"
utype="obscore:Curation.PublisherDID" datatype="char" arraysize="256*">
<DESCRIPTION>publisher dataset identifier</DESCRIPTION>
</FIELD>
<FIELD ID="obs_collection" name="obs_collection" ucd="meta.id" utype="obscore:DataID.Collection"
datatype="char" arraysize="128*">
<DESCRIPTION>short name for the data colection</DESCRIPTION>
</FIELD>
<FIELD ID="facility_name" name="facility_name" ucd="meta.id;instr.tel"
utype="obscore:Provenance.ObsConfig.Facility.name" datatype="char" arraysize="128*">
<DESCRIPTION>telescope name</DESCRIPTION>
</FIELD>
<FIELD ID="instrument_name" name="instrument_name" ucd="meta.id;instr"
utype="obscore:Provenance.ObsConfig.Instrument.name" datatype="char" arraysize="128*">
<DESCRIPTION>instrument name</DESCRIPTION>
</FIELD>
<FIELD ID="obs_id" name="obs_id" ucd="meta.id" utype="obscore:DataID.observationID" datatype="char"
arraysize="128*">
<DESCRIPTION>internal dataset identifier</DESCRIPTION>
</FIELD>
<FIELD ID="dataproduct_type" name="dataproduct_type" ucd="meta.code.class"
utype="obscore:ObsDataset.dataProductType" datatype="char" arraysize="128*">
<DESCRIPTION>type of product</DESCRIPTION>
</FIELD>
<FIELD ID="dataproduct_subtype" name="dataproduct_subtype" ucd="meta.code.class"
utype="obscore:ObsDataset.dataProductSubType" datatype="char" arraysize="128*">
<DESCRIPTION>free sub type of product</DESCRIPTION>
</FIELD>
<FIELD ID="obs_release_date" name="obs_release_date" ucd="time.release" utype="obscore:Curation.releaseDate"
datatype="char" arraysize="23*">
<DESCRIPTION>timestamp of date the data becomes publicly available</DESCRIPTION>
</FIELD>
<FIELD ID="target_name" name="target_name" ucd="meta.id;src" utype="obscore:Target.Name" datatype="char"
arraysize="32*">
<DESCRIPTION>name of intended target</DESCRIPTION>
</FIELD>
<FIELD ID="s_resolution" name="s_resolution" unit="arcsec" ucd="pos.angResolution"
utype="obscore:Char.SpatialAxis.Resolution.refval.value" datatype="double">
<DESCRIPTION>typical spatial resolution</DESCRIPTION>
</FIELD>
<FIELD ID="s_xel1" name="s_xel1" ucd="meta.number" utype="obscore:Char.SpatialAxis.numBins1"
datatype="long">
<DESCRIPTION>dimensions (number of pixels) along one spatial axis</DESCRIPTION>
</FIELD>
<FIELD ID="s_xel2" name="s_xel2" ucd="meta.number" utype="obscore:Char.SpatialAxis.numBins2"
datatype="long">
<DESCRIPTION>dimensions (number of pixels) along the other spatial axis</DESCRIPTION>
</FIELD>
<FIELD ID="t_min" name="t_min" unit="d" ucd="time.start;obs.exposure"
utype="obscore:Char.TimeAxis.Coverage.Bounds.Limits.StartTime" datatype="double">
<DESCRIPTION>start time of observation (MJD)</DESCRIPTION>
</FIELD>
<FIELD ID="t_max" name="t_max" unit="d" ucd="time.end;obs.exposure"
utype="obscore:Char.TimeAxis.Coverage.Bounds.Limits.StopTime" datatype="double">
<DESCRIPTION>end time of observation (MJD)</DESCRIPTION>
</FIELD>
<FIELD ID="t_exptime" name="t_exptime" unit="s" ucd="time.duration;obs.exposure"
utype="obscore:Char.TimeAxis.Coverage.Support.Extent" datatype="double">
<DESCRIPTION>exposure time of observation</DESCRIPTION>
</FIELD>
<FIELD ID="t_resolution" name="t_resolution" unit="s" ucd="time.resolution"
utype="obscore:Char.TimeAxis.Resolution.refval.value" datatype="double">
<DESCRIPTION>typical temporal resolution</DESCRIPTION>
</FIELD>
<FIELD ID="t_xel" name="t_xel" ucd="meta.number" utype="obscore:Char.TimeAxis.numBins" datatype="long">
<DESCRIPTION>dimensions (number of pixels) along the time axis</DESCRIPTION>
</FIELD>
<FIELD ID="em_min" name="em_min" unit="m" ucd="em.wl;stat.min"
utype="obscore:Char.SpectralAxis.Coverage.Bounds.Limits.LoLimit" datatype="double">
<DESCRIPTION>start spectral coordinate value</DESCRIPTION>
</FIELD>
<FIELD ID="em_max" name="em_max" unit="m" ucd="em.wl;stat.max"
utype="obscore:Char.SpectralAxis.Coverage.Bounds.Limits.HiLimit" datatype="double">
<DESCRIPTION>stop spectral coordinate value</DESCRIPTION>
</FIELD>
<FIELD ID="em_res_power" name="em_res_power" ucd="spect.resolution"
utype="obscore:Char.SpectralAxis.Resolution.ResolPower.refval" datatype="double">
<DESCRIPTION>typical spectral resolution</DESCRIPTION>
</FIELD>
<FIELD ID="em_xel" name="em_xel" ucd="meta.number" utype="obscore:Char.SpectralAxis.numBins"
datatype="long">
<DESCRIPTION>dimensions (number of pixels) along the energy axis</DESCRIPTION>
</FIELD>
<FIELD ID="pol_xel" name="pol_xel" ucd="meta.number" utype="obscore:Char.PolarizationAxis.numBins"
datatype="long">
<DESCRIPTION>dimensions (number of pixels) along the polarization axis</DESCRIPTION>
</FIELD>
<FIELD ID="access_url" name="access_url" ucd="meta.ref.url" utype="obscore:Access.Reference" datatype="char"
arraysize="*">
<DESCRIPTION>URL to download the data</DESCRIPTION>
</FIELD>
<FIELD ID="access_estsize" name="access_estsize" unit="kbyte" ucd="phys.size;meta.file"
utype="obscore:Access.Size" datatype="long">
<DESCRIPTION>estimated size of the download</DESCRIPTION>
</FIELD>
<FIELD ID="pol_states" name="pol_states" ucd="meta.code;phys.polarization"
utype="obscore:Char.PolarizationAxis.stateList" datatype="char" arraysize="32*">
<DESCRIPTION>polarization states present in the data</DESCRIPTION>
</FIELD>
<FIELD ID="o_ucd" name="o_ucd" ucd="meta.ucd" utype="obscore:Char.ObservableAxis.ucd" datatype="char"
arraysize="32*">
<DESCRIPTION>UCD describing the observable axis (pixel values)</DESCRIPTION>
</FIELD>
<FIELD ID="access_format" name="access_format" ucd="meta.code.mime" utype="obscore:Access.Format"
datatype="char" arraysize="128*">
<DESCRIPTION>format of the data file(s)</DESCRIPTION>
</FIELD>
<DATA>
<TABLEDATA>
<TR>
<TD>3</TD>
<TD>214.9</TD>
<TD>56.7</TD>
<TD>0.2</TD>
<TD>polygon 215.92 56.25 214.03 56.25 214.03 57.34 215.92 57.34</TD>
<TD>ivo://SKA/SrcNet/APERTIF_DR1/200110007_AP_B037/HI_image_cube3</TD>
<TD>APERTIF_DR1</TD>
<TD>wsrt</TD>
<TD>Apertif</TD>
<TD>200104010</TD>
<TD>cube</TD>
<TD>HI_spectral_cubes</TD>
<TD>2020-01-04T06:38:21Z</TD>
<TD>NGC 5585</TD>
<TD>0.4</TD>
<TD>661</TD>
<TD>661</TD>
<TD>61416.3556875</TD>
<TD>61416.356376527775</TD>
<TD>59.575</TD>
<TD>0.1</TD>
<TD>1218</TD>
<TD>0.211</TD>
<TD>0.217</TD>
<TD>7000000</TD>
<TD>100000</TD>
<TD>4</TD>
<TD>https://raw.githubusercontent.com/VisIVOLab/SKA-Discovery-Service-Mockup/main/DataLink/DataLink_003.xml</TD>
<TD>2128688640</TD>
<TD>I Q U V</TD>
<TD>phot.flux.density;phys.polarization</TD>
<TD>application/x-votable+xml;content=datalink</TD>
</TR>
<TR>
<TD>3</TD>
<TD>214.9</TD>
<TD>56.7</TD>
<TD>0.2</TD>
<TD>polygon 215.92 56.25 214.03 56.25 214.03 57.34 215.92 57.34</TD>
<TD>ivo://SKA/SrcNet/APERTIF_DR1/200110007_AP_B037/HI_image_cube3</TD>
<TD>APERTIF_DR1</TD>
<TD>wsrt</TD>
<TD>Apertif</TD>
<TD>200104010</TD>
<TD>cube</TD>
<TD>HI_spectral_cubes</TD>
<TD>2020-01-04T06:38:21Z</TD>
<TD>NGC 5585</TD>
<TD>0.4</TD>
<TD>661</TD>
<TD>661</TD>
<TD>61416.3556875</TD>
<TD>61416.356376527775</TD>
<TD>59.575</TD>
<TD>0.1</TD>
<TD>1218</TD>
<TD>0.211</TD>
<TD>0.217</TD>
<TD>7000000</TD>
<TD>100000</TD>
<TD>4</TD>
<TD>https://raw.githubusercontent.com/VisIVOLab/SKA-Discovery-Service-Mockup/main/DataLink/DataLink_003.xml</TD>
<TD>2128688640</TD>
<TD>I Q U V</TD>
<TD>phot.flux.density;phys.polarization</TD>
<TD>application/x-votable+xml;content=datalink</TD>
</TR>
</TABLEDATA>
</DATA>
</TABLE>
</RESOURCE>
</VOTABLE>

21
examples/al-obscore.html Normal file
View File

@@ -0,0 +1,21 @@
<!doctype html>
<html>
<head>
</head>
<body>
<div id="aladin-lite-div" style="width: 500px; height: 400px"></div>
<div id='aladin-statsDiv'></div>
<script type="text/javascript" src="./../aladin.js" charset="utf-8"></script>
<script type="text/javascript">
let aladin;
A.init.then(() => {
aladin = A.aladin('#aladin-lite-div', {target: '14 18 16.868 +56 44 29.37', fov: 5});
const catalog = A.catalogFromURL('./ObsCore_003.xml', {onClick: 'showTable'});
aladin.addCatalog(catalog);
});
</script>
</body>
</html>

View File

@@ -18,6 +18,8 @@
aladin.addCatalog(A.catalogFromURL('https://aladin.cds.unistra.fr/AladinLite/doc/API/examples/data/alma-footprints.xml', {}, function(sources) {
sources.forEach(source => {
console.log(A.footprintsFromSTCS(source.data['s_region']))
overlay.addFootprints(A.footprintsFromSTCS(source.data['s_region']))
});
}));

99
examples/datalink.xml Normal file
View File

@@ -0,0 +1,99 @@
<VOTABLE xmlns="http://www.ivoa.net/xml/VOTable/v1.3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="1.4">
<DESCRIPTION>Mockup SKA discovery service response VOTable</DESCRIPTION>
<COOSYS ID="J2000" equinox="2000." epoch="2000" system="eq_FK5" />
<RESOURCE type="results">
<TABLE>
<FIELD name="ID" datatype="char" arraysize="*" ucd="meta.id;meta.main" />
<FIELD name="access_url" datatype="char" arraysize="*" ucd="meta.ref.url" />
<FIELD name="service_def" datatype="char" arraysize="*" ucd="meta.ref" />
<FIELD name="error_message" datatype="char" arraysize="*" ucd="meta.code.error" />
<FIELD name="semantics" datatype="char" arraysize="*" ucd="meta.code" />
<FIELD name="description" datatype="char" arraysize="*" ucd="meta.note" />
<FIELD name="content_type" datatype="char" arraysize="*" ucd="meta.code.mime" />
<FIELD name="content_length" datatype="long" ucd="phys.size;meta.file" unit="byte" />
<FIELD name="content_qualifier" datatype="char" arraysize="*" ucd="meta.code" />
<DATA>
<TABLEDATA>
<TR>
<TD>ivo://SKA/SrcNet/APERTIF_DR1/200110007_AP_B037/HI_image_cube3</TD>
<TD>https://vo.astron.nl/getproduct/APERTIF_DR1/200110007_AP_B037/HI_image_cube3.fits</TD>
<TD><TD />
<TD><TD />
<TD>#this</TD>
<TD>The full dataset</TD>
<TD>image/fits</TD>
<TD>2128688640</TD>
<TD>cube</TD>
</TR>
<TR>
<TD>ivo://SKA/SrcNet/APERTIF_DR1/200110007_AP_B037/HI_image_cube3</TD>
<TD>https://alasky.cds.unistra.fr/hips-cube-services/compute/hips:httpsalasky.cds.unistra.fr;SKA-demo;hips;APERTIF;HiPS_APERTIF_cube_NGC5585-cubic-tiles%7Cexpr:s0_1211</TD>
<TD><TD />
<TD><TD />
<TD>#derived</TD>
<TD>Moment zero map of the cube (in HiPS format)</TD>
<TD>application/hips</TD>
<TD>2128688640</TD>
<TD>image</TD>
</TR>
<TR>
<TD>ivo://SKA/SrcNet/APERTIF_DR1/200110007_AP_B037/HI_image_cube3</TD>
<TD>https://alasky.cds.unistra.fr/hips-cube-services/compute/hips:httpsalasky.cds.unistra.fr;SKA-demo;hips;APERTIF;HiPS_APERTIF_cube_NGC5585-cubic-tiles%7Cexpr:s345_410-(s0_1211;24)</TD>
<TD><TD />
<TD><TD />
<TD>#derived</TD>
<TD>HI Line map derived from the cube (in HiPS format)</TD>
<TD>application/hips</TD>
<TD>2128688640</TD>
<TD>image</TD>
</TR>
<TR>
<TD>ivo://SKA/SrcNet/APERTIF_DR1/200110007_AP_B037/HI_image_cube3</TD>
<TD>https://alasky.cds.unistra.fr/hips-cube-services/color/hips:httpsalasky.cds.unistra.fr;SKA-demo;hips;APERTIF;HiPS_APERTIF_cube_NGC5585%7Cblue:345-365%7Cgreen:365-385%7Cred:385-405</TD>
<TD><TD />
<TD><TD />
<TD>#derived</TD>
<TD>velocity variation color map derived from the cube (in HiPS format)</TD>
<TD>application/hips</TD>
<TD>2128688640</TD>
<TD>image</TD>
</TR>
<TR>
<TD>ivo://SKA/SrcNet/APERTIF_DR1/200110007_AP_B037/HI_image_cube3</TD>
<TD><TD />
<TD>soda-HiPS</TD>
<TD><TD />
<TD>#cutout</TD>
<TD>SODA-HiPS cutout of moment zero map of ivo://SKA/SrcNet/APERTIF_DR1/200110007_AP_B037/HI_image_cube3</TD>
<TD>image/fits</TD>
<TD><TD />
<TD></TD>
</TR>
<TR>
<TD>ivo://SKA/SrcNet/APERTIF_DR1/200110007_AP_B037/HI_image_cube3</TD>
<TD><TD />
<TD>soda-sync</TD>
<TD><TD />
<TD>#cutout</TD>
<TD>SODA-sync cutout of ivo://SKA/SrcNet/APERTIF_DR1/200110007_AP_B037/HI_image_cube3</TD>
<TD>image/fits</TD>
<TD><TD />
<TD></TD>
</TR>
<TR>
<TD>ivo://SKA/SrcNet/APERTIF_DR1/200110007_AP_B037/HI_image_cube3</TD>
<TD><TD />
<TD>soda-async</TD>
<TD><TD />
<TD>#cutout</TD>
<TD>SODA-async cutout of ivo://SKA/SrcNet/APERTIF_DR1/200110007_AP_B037/HI_image_cube3</TD>
<TD>image/fits</TD>
<TD><TD />
<TD></TD>
</TR>
</TABLEDATA>
</DATA>
</TABLE>
</RESOURCE>
</VOTABLE>

View File

@@ -569,9 +569,9 @@ impl WebClient {
/// * `lon` - A longitude in degrees
/// * `lat` - A latitude in degrees
#[wasm_bindgen(js_name = worldToScreen)]
pub fn world_to_screen(&self, lon: f64, lat: f64) -> Option<Box<[f64]>> {
pub fn world_to_screen(&self, lon: f64, lat: f64) -> Option<Box<[i32]>> {
self.app.world_to_screen(lon, lat)
.map(|v| Box::new([v.x, v.y]) as Box<[f64]>)
.map(|v| Box::new([v.x as i32, v.y as i32]) as Box<[i32]>)
}
/// Screen to world unprojection
@@ -820,7 +820,7 @@ impl WebClient {
#[wasm_bindgen(js_name = parseVOTable)]
pub fn parse_votable(&mut self, s: &str) -> Result<JsValue, JsValue> {
let votable: VOTableWrapper<votable::impls::mem::InMemTableDataRows> = votable::votable::VOTableWrapper::from_ivoa_xml_str(s)
.map_err(|_| JsValue::from_str("Error parsing votable"))?;
.map_err(|err| JsValue::from_str(&format!("Error parsing votable: {:?}", err)))?;
let votable = serde_wasm_bindgen::to_value(&votable)
.map_err(|_| JsValue::from_str("cannot convert votable to js type"))?;

View File

@@ -2,9 +2,11 @@
position: relative;
border: 1px solid #ddd;
height: 100%;
/*overflow: hidden;*/
}
/* disable x swipe on chrome, firefox */
/* see. https://stackoverflow.com/questions/30636930/disable-web-page-navigation-on-swipeback-and-forward */
overscroll-behavior-x: none;
}
.aladin-imageCanvas {
position: absolute;
@@ -89,19 +91,31 @@
}
.aladin-measurement-div {
background-color: rgba(255, 255, 255, 0.8);
z-index: 77;
position:absolute;
bottom: 20px;
background-color: rgba(255, 255, 255, 0.8);
font-family: monospace;
font-size: 12px;
display: none;
width: 100%;
overflow: auto;
/* Allow scrolling but disable scroll bar */
-ms-overflow-style: none; /* for Internet Explorer, Edge */
scrollbar-width: none; /* for Firefox */
overflow-y: scroll;
/* disable x swipe on chrome, firefox */
overscroll-behavior-x: none;
}
.aladin-measurement-div::-webkit-scrollbar {
display: none; /* for Chrome, Safari, and Opera */
}
.aladin-measurement-div table {
padding: 2px 4px 2px 4px;
table-layout: fixed;
white-space: nowrap;
}
@@ -221,10 +235,6 @@ word-wrap:break-word;
overflow-y: auto;
}
element {
}
.aladin-box {
display: none;
z-index: 30;

View File

@@ -68,6 +68,7 @@ import $ from 'jquery';
// Import aladin css inside the project
import './../css/aladin.css';
import { VOTable } from "./vo/VOTable.js";
export let Aladin = (function () {
@@ -1832,8 +1833,8 @@ Aladin.prototype.setReduceDeformations = function (reduce) {
}
// API
A.footprintsFromSTCS = function (stcs) {
var footprints = Overlay.parseSTCS(stcs);
A.footprintsFromSTCS = function (stcs, options) {
var footprints = Overlay.parseSTCS(stcs, options);
return footprints;
}
@@ -1861,8 +1862,25 @@ A.catalogFromURL = function (url, options, successCallback, useProxy) {
var catalog = A.catalog(options);
Catalog.parseVOTable(
url,
function (sources) {
function (sources, footprints, fields) {
if (fields["access_url"]) {
// It is an obsore table pointing to a datalink table
catalog.addFieldClickCallback("access_url", (url) => {
VOTable.parse(
"./examples/datalink.xml",
(fields, rows) => {
let sources = [];
let footprints = [];
console.log(fields, rows)
}
)
});
}
catalog.addFootprints(footprints)
catalog.addSources(sources);
if (successCallback) {
successCallback(sources);
}
@@ -1875,24 +1893,6 @@ A.catalogFromURL = function (url, options, successCallback, useProxy) {
return catalog;
};
A.obscoreFromURL = function (url, options, successCallback, useProxy) {
options = options.color || Obscore.COLOR;
let catalog = A.catalog(options);
Obscore.parseVOTable(
url,
function (sources) {
catalog.addSources(sources);
if (successCallback) {
successCallback(sources);
}
}
);
return catalog;
};
// API
// @param target: can be either a string representing a position or an object name, or can be an object with keys 'ra' and 'dec' (values being in decimal degrees)
A.catalogFromSimbad = function (target, radius, options, successCallback) {

View File

@@ -35,6 +35,7 @@ import { Utils } from "./Utils.js";
import { AladinUtils } from "./AladinUtils.js";
import { Coo } from "./libs/astro/coo.js";
import { ALEvent } from "./events/ALEvent.js";
import { Obscore } from "./vo/Obscore.js";
import { VOTable } from "./vo/VOTable.js";
import $ from 'jquery';
@@ -60,11 +61,11 @@ export let Catalog = (function() {
this.raField = options.raField || undefined; // ID or name of the field holding RA
this.decField = options.decField || undefined; // ID or name of the field holding dec
this.fieldsClickCallbacks = {}; // callbacks when the user clicks on a cell in the measurement table associated
this.indexationNorder = 5; // à quel niveau indexe-t-on les sources
this.sources = [];
this.footprints = [];
//this.hpxIdx = new HealpixIndex(this.indexationNorder);
//this.hpxIdx.init();
this.displayLabel = options.displayLabel || false;
this.labelColor = options.labelColor || this.color;
@@ -266,149 +267,92 @@ export let Catalog = (function() {
};
Catalog.parseFields = function(fields, raField, decField) {
// This votable is not an obscore one
let [raFieldIdx, decFieldIdx] = findRADecFields(fields, raField, decField);
let parsedFields = {};
let fieldIdx = 0;
fields.forEach((field) => {
let key = field.name ? field.name : field.id;
let nameField;
if (fieldIdx == raFieldIdx) {
nameField = 'ra';
} else if (fieldIdx == decFieldIdx) {
nameField = 'dec';
} else {
nameField = key;
}
parsedFields[nameField] = {
name: key,
idx: fieldIdx,
};
fieldIdx++;
})
return parsedFields;
};
// return an array of Source(s) from a VOTable url
// callback function is called each time a TABLE element has been parsed
Catalog.parseVOTable = function(url, callback, maxNbSources, useProxy, raField, decField) {
/*
// adapted from votable.js
function getPrefix($xml) {
var prefix;
// If Webkit chrome/safari/... (no need prefix)
if($xml.find('RESOURCE').length>0) {
prefix = '';
}
else {
// Select all data in the document
prefix = $xml.find("*").first();
VOTable.parse(
url,
(fields, rows) => {
let sources = [];
let footprints = [];
if (prefix.length==0) {
return '';
}
rows.every(row => {
let ra, dec, region;
var mesures = {};
// get name of the first tag
prefix = prefix.prop("tagName");
for (const [fieldName, field] of Object.entries(fields)) {
if (fieldName === 's_region') {
// Obscore s_region param
region = row[field.idx];
} else if (fieldName === 'ra' || fieldName === 's_ra') {
ra = row[field.idx]
} else if (fieldName === 'dec' || fieldName === 's_dec') {
dec = row[field.idx]
}
var idx = prefix.indexOf(':');
prefix = prefix.substring(0, idx) + "\\:";
}
return prefix;
}
function doParseVOTable(xml, callback) {
xml = xml.replace(/^\s+/g, ''); // we need to trim whitespaces at start of document
var attributes = ["name", "ID", "ucd", "utype", "unit", "datatype", "arraysize", "width", "precision"];
var fields = [];
var k = 0;
var $xml = $($.parseXML(xml));
var prefix = getPrefix($xml);
$xml.find(prefix + "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);
var key = field.name;
mesures[key] = row[field.idx];
}
if (ra && dec) {
const source = new Source(ra, dec, mesures);
sources.push(source);
if (maxNbSources && sources.length == maxNbSources) {
return false;
}
}
if (region) {
let footprint = A.footprintsFromSTCS(region, {lineWidth: 2});
footprints = footprints.concat(footprint);
}
return true;
})
if (callback) {
callback(sources, footprints, fields);
}
if ( ! f.ID) {
f.ID = "col_" + k;
}
fields.push(f);
k++;
});
var raDecFieldIdxes = findRADecFields(fields, raField, decField);
var raFieldIdx, decFieldIdx;
raFieldIdx = raDecFieldIdxes[0];
decFieldIdx = raDecFieldIdxes[1];
var sources = [];
var coo = new Coo();
var ra, dec;
$xml.find(prefix + "TR").each(function() {
var mesures = {};
var k = 0;
$(this).find(prefix + "TD").each(function() {
var key = fields[k].name ? fields[k].name : fields[k].id;
mesures[key] = $(this).text();
k++;
});
var keyRa = fields[raFieldIdx].name ? fields[raFieldIdx].name : fields[raFieldIdx].id;
var keyDec = fields[decFieldIdx].name ? fields[decFieldIdx].name : fields[decFieldIdx].id;
if (Utils.isNumber(mesures[keyRa]) && Utils.isNumber(mesures[keyDec])) {
ra = parseFloat(mesures[keyRa]);
dec = parseFloat(mesures[keyDec]);
} else {
coo.parse(mesures[keyRa] + " " + mesures[keyDec]);
ra = coo.lon;
dec = coo.lat;
}
sources.push(new Source(ra, dec, mesures));
if (maxNbSources && sources.length == maxNbSources) {
return false; // break the .each loop
}
});
if (callback) {
callback(sources);
}
}
*/
new VOTable(url, (votable) => {
let sources = [];
votable.votable.get("resources")
.forEach((resource) => {
let tables = resource.get("tables")
tables.forEach((table) => {
let fields = table.get("elems")
.map((field) => {
// convert a map into a javascript object
return Object.fromEntries(field);
})
var raDecFieldIdxes = findRADecFields(fields, raField, decField);
const raFieldIdx = raDecFieldIdxes[0];
const decFieldIdx = raDecFieldIdxes[1];
let data = table.get("data");
let rows = data.get("rows");
rows.every(row => {
var mesures = {};
let idxField = 0;
for (const field of fields) {
var key = field.name ? field.name : field.id;
mesures[key] = row[idxField];
idxField += 1;
}
const ra = row[raFieldIdx];
const dec = row[decFieldIdx];
sources.push(new Source(ra, dec, mesures));
if (maxNbSources && sources.length == maxNbSources) {
return false;
}
return true;
})
})
});
if (callback) {
callback(sources);
}
})
},
raField,
decField
)
};
Catalog.prototype.addFieldClickCallback = function(field, callback) {
this.fieldsClickCallbacks[field] = callback;
}
// API
Catalog.prototype.updateShape = function(options) {
options = options || {};
@@ -557,13 +501,7 @@ export let Catalog = (function() {
if (this._shapeIsFunction) {
ctx.save();
}
/*var sourcesInView = [];
for (var k=0, len = this.sources.length; k<len; k++) {
var inView = Catalog.drawSource(this, this.sources[k], ctx, frame, width, height, largestDim, zoomFactor);
if (inView) {
sourcesInView.push(this.sources[k]);
}
}*/
const sourcesInView = this.drawSources(ctx, width, height);
if (this._shapeIsFunction) {
@@ -578,6 +516,9 @@ export let Catalog = (function() {
Catalog.drawSourceLabel(this, s, ctx);
})
}
// Draw the footprints
this.drawFootprints(ctx);
};
Catalog.prototype.drawSources = function(ctx, width, height) {
@@ -630,11 +571,6 @@ export let Catalog = (function() {
s.popup.setPosition(s.x, s.y);
}
// Draw the source footprint if there is any
if (s.footprint) {
s.footprint.draw(ctx, this.view);
}
return true;
}
}
@@ -655,7 +591,13 @@ export let Catalog = (function() {
ctx.fillText(label, s.x, s.y);
};
Catalog.prototype.drawFootprints = function(ctx) {
this.footprints.forEach((f) => {
f.draw(ctx, this.view)
});
};
// callback function to be called when the status of one of the sources has changed
Catalog.prototype.reportChange = function() {
this.view && this.view.requestRedraw();

View File

@@ -43,6 +43,8 @@ export let MeasurementTable = (function() {
this.numPages = 1;
this.numRowsByPage = 5;
this.columnClickAction = {};
$(aladinLiteDiv).append(this.divEl);
}
@@ -51,7 +53,21 @@ export let MeasurementTable = (function() {
rows.forEach(row => {
tbody += '<tr>'
for (let key in row.data) {
tbody += '<td>' + row.data[key] + '</td>';
// check the type here
const val = row.data[key];
tbody += '<td class="' + key + '">'
if (typeof(val) === "string") {
try {
let url = new URL(val);
let link = '<a href=' + url + '>' + url + '</a>';
tbody += link;
} catch(e) {
tbody += val
}
} else {
tbody += val
}
tbody += '</td>'
}
tbody += '</tr>';
});
@@ -67,13 +83,27 @@ export let MeasurementTable = (function() {
let tbody = this.divEl[0].querySelector(".content");
tbody.innerHTML = MeasurementTable.updateBodyTable(fullRows.slice(rowIdxStart, rowIdxStart + this.numRowsByPage));
if (this.fieldsClickCallbacks) {
Object.entries(this.fieldsClickCallbacks)
.forEach(([key, callback]) => {
this.divEl[0].querySelectorAll("." + key).forEach((e) => {
e.addEventListener('click', (e) => {
callback(e.target.innerText)
e.preventDefault();
}, false)
})
});
}
// recompute page idx
let pageIdxElt = this.divEl[0].querySelector("#pageIdx");
pageIdxElt.innerHTML = '<p id="pageIdx" style="display: inline-block; margin: 0">' + this.curPage + '/' + this.numPages + '</p>';
}
// show measurement associated with a given source
MeasurementTable.prototype.showMeasurement = function(rows) {
MeasurementTable.prototype.showMeasurement = function(rows, table) {
this.fieldsClickCallbacks = table.fieldsClickCallbacks;
// compute the number of pages
this.numPages = Math.floor(rows.length / this.numRowsByPage);
if (rows.length % this.numRowsByPage > 0) {
@@ -89,13 +119,25 @@ export let MeasurementTable = (function() {
thead += '</tr></thead>';
let tbody = MeasurementTable.updateBodyTable(rows.slice(0, this.numRowsByPage));
this.divEl.append('<table>' + thead + tbody + '</table>');
// Add the callbacks to the cells
if (this.fieldsClickCallbacks) {
Object.entries(this.fieldsClickCallbacks)
.forEach(([key, callback]) => {
this.divEl[0].querySelectorAll("." + key).forEach((e) => {
e.addEventListener('click', (e) => {
callback(e.target.innerText)
e.preventDefault();
}, false)
})
});
}
if (this.numPages > 1) {
this.divEl.append('<div class="footer"><button id="prevButton" style="display: inline-block">Previous</button><button id="nextButton" style="display: inline-block">Next</button><p id="pageIdx" style="display: inline-block; margin: 0">' + this.curPage + '/' + this.numPages + '</p></div>');
document.querySelector('#nextButton').addEventListener(
this.divEl[0].querySelector('#nextButton').addEventListener(
'click',
() => {
this.curPage++;
@@ -107,7 +149,8 @@ export let MeasurementTable = (function() {
}
,false
);
document.querySelector('#prevButton').addEventListener(
this.divEl[0].querySelector('#prevButton').addEventListener(
'click',
() => {
this.curPage--;

View File

@@ -74,7 +74,9 @@ export let Overlay = (function() {
};
// return an array of Footprint from a STC-S string
Overlay.parseSTCS = function(stcs) {
Overlay.parseSTCS = function(stcs, options) {
options = options || {};
var footprints = [];
var parts = stcs.match(/\S+/g);
var k = 0, len = parts.length;
@@ -99,7 +101,9 @@ export let Overlay = (function() {
curPolygon.push([ra, dec]);
k += 2;
}
footprints.push(A.polygon(curPolygon, {closed: true}));
options.closed = true;
footprints.push(A.polygon(curPolygon, options));
}
}
else if (s=='circle') {
@@ -114,7 +118,7 @@ export let Overlay = (function() {
dec = parseFloat(parts[k+2]);
radiusDegrees = parseFloat(parts[k+3]);
footprints.push(A.circle(ra, dec, radiusDegrees));
footprints.push(A.circle(ra, dec, radiusDegrees, options));
k += 3;
}

View File

@@ -43,8 +43,6 @@ export let Source = (function() {
this.useMarkerDefaultIcon = (options && options.useMarkerDefaultIcon!==undefined) ? options.useMarkerDefaultIcon : true;
}
this.footprint = (options && options.footprint) || undefined;
this.isShowing = true;
this.isSelected = false;
};
@@ -100,7 +98,7 @@ export let Source = (function() {
if (this.catalog.onClick=='showTable') {
this.select();
view.aladin.measurementTable.showMeasurement([this]);
view.aladin.measurementTable.showMeasurement([this], this.catalog);
}
else if (this.catalog.onClick=='showPopup') {

View File

@@ -572,12 +572,13 @@ export let View = (function () {
view.dragy - view.selectStartCoo.y
);
selectedObjects.forEach((obj) => {
obj.select()
});
console.log(selectedObjects)
view.aladin.measurementTable.showMeasurement(selectedObjects);
selectedObjects.forEach((obj) => obj.select());
if (selectedObjects.length > 0) {
let table = selectedObjects[0].catalog;
view.aladin.measurementTable.showMeasurement(selectedObjects, table);
}
view.selectedObjects = selectedObjects;
view.aladin.fire(

View File

@@ -71,79 +71,56 @@
'instrument_name': { name: 'instrument_name', ucd: 'meta.id;instr', utype: 'Provenance.ObsConfig.Instrument.name', units: null },
}
Obscore.clickOnAccessUrlAction = function(accessUrl) {
// Parse the datalink as a votable
VOTable.parse(accessUrl, (fields, rows) => {
console.log(fields)
})
}
Obscore.COLOR = '#004500'
function Obscore() {};
Obscore.parseVOTable = function(url, callback) {
new VOTable(url, (votable) => {
votable.votable.get("resources")
.forEach((resource) => {
let tables = resource.get("tables")
tables.forEach((table) => {
let fields = table.get("elems")
.map((field) => {
// convert a map into a javascript object
return Object.fromEntries(field);
})
Obscore.parseFields = function(fields) {
let parsedFields = {};
let obsCoreFieldIndices = {};
// Check for mandatory fields
['s_ra', 's_dec', 'access_url', 's_region']
.map((mandatoryFieldName) => {
const mandatoryField = Obscore.MANDATORY_FIELDS[mandatoryFieldName];
const raField = Obscore.MANDATORY_FIELDS['s_ra'];
const decField = Obscore.MANDATORY_FIELDS['s_dec'];
const regionField = Obscore.MANDATORY_FIELDS['s_region'];
const accessUrlField = Obscore.MANDATORY_FIELDS['access_url'];
const fieldIdx = Obscore.findMandatoryField(fields,
mandatoryField.name,
mandatoryField.ucd,
mandatoryField.utype
);
let raFieldIdx = Obscore.findMandatoryField(fields, raField.name, raField.ucd, raField.utype);
let decFieldIdx = Obscore.findMandatoryField(fields, decField.name, decField.ucd, decField.utype);
let regionFieldIdx = Obscore.findMandatoryField(fields, regionField.name, regionField.ucd, regionField.utype);
let accessUrlFieldIdx = Obscore.findMandatoryField(fields, accessUrlField.name, accessUrlField.ucd, accessUrlField.utype);
let field = fields[fieldIdx];
let key = field.name ? field.name : field.id;
let fieldIdx = 0;
fields.forEach((field) => {
let key = field.name ? field.name : field.id;
obsCoreFieldIndices[mandatoryFieldName] = {
columnName: key,
idx: fieldIdx,
};
})
let nameField;
if (fieldIdx == raFieldIdx) {
nameField = 's_ra';
} else if (fieldIdx == decFieldIdx) {
nameField = 's_dec';
} else if (fieldIdx == regionFieldIdx) {
nameField = 's_region';
} else if (fieldIdx == accessUrlFieldIdx) {
nameField = 'access_url';
} else {
nameField = key;
}
// At this point we sure know we have an obscore table
let rows = table.get("data").get("rows");
// We compute the obscore sources
let sources = [];
let footprints = [];
const raFieldIdx = obsCoreFieldIndices['s_ra'].idx;
const decFieldIdx = obsCoreFieldIndices['s_dec'].idx;
const sRegionIdx = obsCoreFieldIndices['s_region'].idx;
parsedFields[nameField] = {
name: key,
idx: fieldIdx,
};
rows.forEach(row => {
var mesures = {};
let idxField = 0;
for (const field of fields) {
var key = field.name ? field.name : field.id;
mesures[key] = row[idxField];
idxField += 1;
}
const ra = row[raFieldIdx];
const dec = row[decFieldIdx];
const region = row[sRegionIdx];
let footprint = A.footprintsFromSTCS(region)[0];
sources.push(new Source(ra, dec, mesures, {footprint: footprint}));
})
// Give the source list and a table of correspondance of mandatory obscore fields to source fields
if (callback) {
callback(sources, footprints)
}
})
});
fieldIdx++;
})
return parsedFields;
};

View File

@@ -26,6 +26,8 @@
*
*****************************************************************************/
import { ALEvent } from "../events/ALEvent.js";
import { Catalog } from "../Catalog.js";
import { Obscore } from "./Obscore.js";
export let VOTable = (function() {
@@ -39,6 +41,42 @@ export let VOTable = (function() {
}});
})
};
VOTable.parse = function (url, callback, raField, decField) {
fetch(url)
.then((response) => response.text())
.then((xml) => {
ALEvent.AL_USE_WASM.dispatchedTo(document.body, {callback: (wasm) => {
let votable = wasm.parseVOTable(xml);
votable.votable.get("resources")
.forEach((resource) => {
let tables = resource.get("tables")
tables.forEach((table) => {
let fields = table.get("elems")
.map((field) => {
// convert a map into a javascript object
return Object.fromEntries(field);
})
try {
fields = Obscore.parseFields(fields);
} catch(e) {
// It is not an obscore table
fields = Catalog.parseFields(fields, raField, decField);
}
let data = table.get("data");
let rows = data.get("rows");
callback(fields, rows)
})
})
}
})
})
};
// return an array of Source(s) from a VOTable url
// callback function is called each time a TABLE element has been parsed