Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## Fixed

- Overlays are displayed on the generated HTML static file because it is passed with
traitlets instead of being sent with events. This fixes documentation examples [#151].
- projection change through the UI now change the value of the `projection` traitlet [#151].

## [0.7.0]

## Added
Expand Down
2 changes: 1 addition & 1 deletion examples/12_Planetary_surveys.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
"id": "35ea9256",
"metadata": {},
"source": [
"The target does not return a `SkyCoord` object anymore, since we don't represent the sky here. It is a couple of `Longitude` and `Latitude`."
"The target does not return a `SkyCoord` object anymore, since we don't represent the sky here. It is a tuple of `Longitude` and `Latitude`."
]
},
{
Expand Down
92 changes: 82 additions & 10 deletions js/models/event_handler.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
import MessageHandler from "./message_handler";
import { divNumber, setDivNumber, Lock, setDivHeight } from "../utils";
import {
divNumber,
setDivNumber,
Lock,
setDivHeight,
isLiveSession,
} from "../utils";

export default class EventHandler {
/**
Expand Down Expand Up @@ -118,8 +124,12 @@ export default class EventHandler {
this.model.save_changes();
});

this.aladin.on("projectionChanged", () => {
this.aladin.on("projectionChanged", (projection) => {
this.model.set("projection", projection);

this.updateWCS();
this.update2AxisFoV();

this.model.save_changes();
});

Expand Down Expand Up @@ -235,6 +245,9 @@ export default class EventHandler {
this.model.save_changes();
});

// Detect environment
const isLive = isLiveSession(this.model);

/* ----------------------- */
/* Traits listeners */
/* ----------------------- */
Expand Down Expand Up @@ -270,9 +283,11 @@ export default class EventHandler {
},
/* Rotation control */
_rotation: (rotation) => {
if (rotation === this.aladin.getRotation()) {
return;
}
// And propagate it to Aladin Lite
this.aladin.setRotation(rotation);

// Update WCS and FoV only if this is the last div
this.updateWCS();
this.update2AxisFoV();
Expand Down Expand Up @@ -304,8 +319,65 @@ export default class EventHandler {
overlay.setAlpha(opacity);
}
},
colormap: (colormap) => {
this.aladin.getBaseImageLayer().setColormap(colormap);
},
projection: (projection) => {
this.aladin.setProjection(projection);

this.updateWCS();
this.update2AxisFoV();
this.model.save_changes();
},
};

// Overlays traitlet listening
const handleOverlay = (overlay) => {
if (overlay.action === "add") {
switch (overlay.type) {
case "table":
// A table is transcripted to an ArrayBuffer (thanks to widget_serialization)
this.messageHandler.handleAddTable(overlay);
break;
case "catalog_from_url":
this.messageHandler.handleAddCatalogFromURL(overlay);
break;
case "MOC_from_url":
this.messageHandler.handleAddMOCFromURL(overlay);
break;
case "MOC_from_dict":
this.messageHandler.handleAddMOCFromDict(overlay);
break;
case "regions_stcs":
this.messageHandler.handleAddGraphicOverlay(overlay);
break;
case "regions":
this.messageHandler.handleAddGraphicOverlay(overlay);
break;
case "fits-image":
this.messageHandler.handleAddFitsImage(overlay);
break;
default:
break;
}
} else {
// TODO: remove overlay
}
};

if (isLive) {
// Default behavior: do not listen for overlays but _overlay_patch when ipyaladin
// is connected to a jupyter/jupyterlab session (i.e. not exported to HTML)
this.traitHandlers["_overlay_patch"] = handleOverlay;
} else {
// static HTML file export (i.e. example HTML documentation files or HTML export of a notebook)
this.traitHandlers["overlays"] = (overlays) => {
for (const overlay of overlays) {
handleOverlay(overlay);
}
};
}

for (var trait in this.traitHandlers) {
let handler = this.traitHandlers[trait];
this.model.on("change:" + trait, (_, value) => handler(value));
Expand All @@ -316,15 +388,15 @@ export default class EventHandler {
this.eventHandlers = {
add_marker: this.messageHandler.handleAddMarker,
save_view_as_image: this.messageHandler.handleSaveViewAsImage,
add_fits: this.messageHandler.handleAddFits,
add_catalog_from_URL: this.messageHandler.handleAddCatalogFromURL,
add_MOC_from_URL: this.messageHandler.handleAddMOCFromURL,
add_MOC_from_dict: this.messageHandler.handleAddMOCFromDict,
add_overlay: this.messageHandler.handleAddOverlay,
change_colormap: this.messageHandler.handleChangeColormap,
get_JPG_thumbnail: this.messageHandler.handleGetJPGThumbnail,
trigger_selection: this.messageHandler.handleTriggerSelection,
add_table: this.messageHandler.handleAddTable,
//add_fits: this.messageHandler.handleAddFits,
//add_catalog_from_URL: this.messageHandler.handleAddCatalogFromURL,
//add_MOC_from_URL: this.messageHandler.handleAddMOCFromURL,
//add_MOC_from_dict: this.messageHandler.handleAddMOCFromDict,
//add_overlay: this.messageHandler.handleAddGraphicOverlay,
//add_table: this.messageHandler.handleAddTable,
//change_colormap: this.messageHandler.handleChangeColormap,
};

this.model.on("msg:custom", (msg, buffers) => {
Expand Down
40 changes: 18 additions & 22 deletions js/models/message_handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,11 @@ export default class MessageHandler {
);
}

handleAddFits(msg, buffers) {
const options = convertOptionNamesToCamelCase(msg["options"] || {});
handleAddFitsImage(overlay) {
const options = convertOptionNamesToCamelCase(overlay["options"] || {});
if (!options.name)
options.name = `image_${String(++imageCount).padStart(3, "0")}`;
const buffer = buffers[0];
const buffer = overlay.data.buffer;
const blob = new Blob([buffer], { type: "application/octet-stream" });
const url = URL.createObjectURL(blob);
const image = this.aladin.createImageFITS(url, options, (ra, dec) => {
Expand All @@ -64,25 +64,25 @@ export default class MessageHandler {
this.aladin.setOverlayImageLayer(image, options.name);
}

handleAddCatalogFromURL(msg) {
const options = convertOptionNamesToCamelCase(msg["options"] || {});
this.aladin.addCatalog(A.catalogFromURL(msg["votable_URL"], options));
handleAddCatalogFromURL(catalog) {
const options = convertOptionNamesToCamelCase(catalog["options"] || {});
this.aladin.addCatalog(A.catalogFromURL(catalog["data"], options));
}

handleAddMOCFromURL(msg) {
const options = convertOptionNamesToCamelCase(msg["options"] || {});
this.aladin.addMOC(A.MOCFromURL(msg["moc_URL"], options));
handleAddMOCFromURL(moc) {
const options = convertOptionNamesToCamelCase(moc["options"] || {});
this.aladin.addMOC(A.MOCFromURL(moc["data"], options));
}

handleAddMOCFromDict(msg) {
const options = convertOptionNamesToCamelCase(msg["options"] || {});
this.aladin.addMOC(A.MOCFromJSON(msg["moc_dict"], options));
handleAddMOCFromDict(moc) {
const options = convertOptionNamesToCamelCase(moc["options"] || {});
this.aladin.addMOC(A.MOCFromJSON(moc["data"], options));
}

handleAddOverlay(msg) {
const regions = msg["regions_infos"];
handleAddGraphicOverlay(graphicOverlay) {
const regions = graphicOverlay["data"];
const graphic_options = convertOptionNamesToCamelCase(
msg["graphic_options"] || {},
graphicOverlay["options"] || {},
);
if (!graphic_options["color"]) graphic_options["color"] = "red";
const overlay = A.graphicOverlay(graphic_options);
Expand Down Expand Up @@ -132,10 +132,6 @@ export default class MessageHandler {
}
}

handleChangeColormap(msg) {
this.aladin.getBaseImageLayer().setColormap(msg["colormap"]);
}

handleGetJPGThumbnail() {
this.aladin.exportAsPNG();
}
Expand All @@ -147,15 +143,15 @@ export default class MessageHandler {
this.aladin.select(selectionType);
}

handleAddTable(msg, buffers) {
const options = convertOptionNamesToCamelCase(msg["options"] || {});
handleAddTable(table) {
const options = convertOptionNamesToCamelCase(table["options"] || {});
const circleOptions = convertOptionNamesToCamelCase(
options.circleError || {},
);
const ellipseOptions = convertOptionNamesToCamelCase(
options.ellipseError || {},
);
const buffer = buffers[0].buffer;
const buffer = table.data.buffer;
const decoder = new TextDecoder("utf-8");
const blob = new Blob([decoder.decode(buffer)]);
const url = URL.createObjectURL(blob);
Expand Down
17 changes: 17 additions & 0 deletions js/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,28 @@ function setDivHeight(height, div) {
}
}

// This is needed to know whether ipyaladin is run
// from a live session or from a static export (HTML)
// Depending on that, we will listen to specific overlay traitlets.
// See the overlays and _overlay_patch traitlets definition for more informations.
function isLiveSession(model) {
// ipywidgets v7/v8: model.comm is a Comm in live sessions, null/undefined in static HTML
if (model && model.comm && typeof model.comm.send === "function") return true;

// Extra heuristics if needed (some frontends expose a kernel on the manager)
const mgr = model?.widget_manager;
if (mgr?.kernel) return true; // classic jupyter notebook
if (mgr?.context?.sessionContext?.session?.kernel) return true; // JupyterLab

return false; // static export (HTML)
}

export {
snakeCaseToCamelCase,
convertOptionNamesToCamelCase,
Lock,
divNumber,
setDivNumber,
setDivHeight,
isLiveSession,
};
4 changes: 4 additions & 0 deletions js/widget.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ function render({ model, el }) {
const wcs = { ...aladin.getViewWCS() };
model.set("_wcs", wcs);

// send colormap to python
const cmap = aladin.getBaseImageLayer().getColorCfg().colormap;
model.set("colormap", cmap);

// Tell the widget is loaded so that all stored calls waiting can be executed
model.set("_is_loaded", true);
model.save_changes();
Expand Down
34 changes: 16 additions & 18 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading