diff --git a/CHANGELOG.md b/CHANGELOG.md index 879a12e5..5989cd58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,9 +18,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Allow WCS and 2 axis FoV recovery from `wcs` and `fov_xy` properties (#96) - monochromatic FITS images can be added to the view with `ipyaladin.Aladin.add_fits`. The method accepts `astropy.io.fits.HDUList`, `pathlib.Path`, or `string` representing paths (#86) +### Changed + +- Upgrade Aladin Lite version to 3.4.5-beta (#96) + ## [0.4.0] ### Added @@ -40,7 +45,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Change the jslink target trait from `target` to `shared_target` (#80) - Change the jslink fov trait from `fov` to `shared_fov` (#83) -- Upgrade Aladin Lite version to 3.4.1-beta (#88) +- Upgrade Aladin Lite version to 3.4.4-beta (#88) - Add support for list of strings in `add_overlay_from_stcs` (#88) ### Deprecated diff --git a/README.md b/README.md index 1588d45e..bdf8388b 100644 --- a/README.md +++ b/README.md @@ -56,10 +56,11 @@ Ipyaladin brings [Aladin Lite](https://github.com/cds-astro/aladin-lite) into no Correspondence table between ipyaladin versions and Aladin Lite versions: -| ipyaladin | Aladin-Lite | -| --------- | ----------- | -| 0.3.0 | 3.3.3-dev | -| 0.4.0 | 3.4.4-beta | +| ipyaladin | Aladin-Lite | +| ---------- | ----------- | +| Unreleased | 3.4.5-beta | +| 0.4.0 | 3.4.4-beta | +| 0.3.0 | 3.3.3-dev | > [!TIP] > This can always be read like so diff --git a/examples/11_Extracting_information_from_the_view.ipynb b/examples/11_Extracting_information_from_the_view.ipynb new file mode 100644 index 00000000..39fc3ab4 --- /dev/null +++ b/examples/11_Extracting_information_from_the_view.ipynb @@ -0,0 +1,340 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "98601fdf-e2df-46c9-b039-710e45aabfdc", + "metadata": {}, + "source": [ + "# Retrieving data from the current widget's view\n", + "\n", + "So far, we've seen how to send information (tables, MOCs, ...) into the widget. The other way also works! This notebook contains a list of methods to extract diverse information about the current view. However, here are several information about retrieving data from the widget:\n", + "\n", + "- when the view is modified programmatically (*i.e.* not by clicking on the buttons), the update of its properties is done between cell execution. This means that you'll see a `WidgetCommunicationError` when you try to modify the view and retrieve information about it in the **same** cell. Simply switch the property-reading to the next cell and everything will work as intended!\n", + "- if you generate different views of the same `Aladin()` instances -- either by calling `display` multiple times, or by using the `Create new view for cell output` button in Jupyter, the information contained in `wcs` and `fov_xy` will always correspond to the **most recently** created view." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "initial_id", + "metadata": {}, + "outputs": [], + "source": [ + "from ipyaladin import Aladin" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "2e62d34eb8543145", + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "68df3ccd903340b4aa8af8aad92c0c20", + "version_major": 2, + "version_minor": 1 + }, + "text/plain": [ + "Aladin(init_options=['_fov', '_height', '_target', 'background_color', 'coo_frame', 'full_screen', 'grid_color…" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "aladin = Aladin(fov=5, height=600, target=\"M31\")\n", + "aladin" + ] + }, + { + "cell_type": "markdown", + "id": "ae0a5496-d621-49ef-a11a-3578c272ce92", + "metadata": {}, + "source": [ + "## Getting the current WCS\n", + "\n", + "The World Coordinates System (WCS) describes the field of view, the projection, and it's rotation. It is returned as an `astropy.coordinates.WCS` object." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "84153657cb7cd837", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "WCS Keywords\n", + "\n", + "Number of WCS axes: 2\n", + "CTYPE : 'RA---SIN' 'DEC--SIN' \n", + "CRVAL : 10.6847083 41.26875 \n", + "CRPIX : 843.0 300.5 \n", + "PC1_1 PC1_2 : 1.0 0.0 \n", + "PC2_1 PC2_2 : 0.0 1.0 \n", + "CDELT : -0.0029673590504451 0.002967359050445104 \n", + "NAXIS : 1685 600" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "aladin.wcs # Recover the current WCS" + ] + }, + { + "cell_type": "markdown", + "id": "998def1f-3963-405b-8be2-6d4ef4012634", + "metadata": {}, + "source": [ + "If you edit the view either by modifiing the widget through its interface, or programmatically: " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "a63f210b-3a64-4860-8e70-42a4c66378fa", + "metadata": {}, + "outputs": [], + "source": [ + "aladin.height = 800\n", + "aladin.survey = \"CDS/P/PLANCK/R2/HFI/color\"\n", + "aladin.target = \"LMC\"\n", + "aladin.frame = \"Galactic\"\n", + "aladin.fov = 50" + ] + }, + { + "cell_type": "markdown", + "id": "9c2221f3-6ecc-46d6-9d53-5dbefa71326d", + "metadata": {}, + "source": [ + "The wcs is updated and you can print its new value in the **next cell**:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "2ddc9637-b5c3-4412-8435-2302b6d86816", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "WCS Keywords\n", + "\n", + "Number of WCS axes: 2\n", + "CTYPE : 'RA---SIN' 'DEC--SIN' \n", + "CRVAL : 80.89416999999995 -69.75611 \n", + "CRPIX : 843.0 400.5 \n", + "PC1_1 PC1_2 : 1.0 0.0 \n", + "PC2_1 PC2_2 : 0.0 1.0 \n", + "CDELT : -0.0029673590504451 0.002967359050445104 \n", + "NAXIS : 1685 800" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "aladin.wcs" + ] + }, + { + "cell_type": "markdown", + "id": "f5add3a2-be30-488e-86df-426338b98f5d", + "metadata": {}, + "source": [ + "If you try to recover the value in the **same cell**, you'll get a `WidgetCommunicationError` error. This is because the calculation of the WCS is done by Aladin Lite *between* cell executions. \n", + "\n", + "## Getting the field of view\n", + "\n", + "The field of view is printed in the bottom left corner of the view. You can grab the two values with:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "9595ae02388b245a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(, )" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "aladin.fov_xy # Recover the current field of view for the x and y axis" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.8" + }, + "widgets": { + "application/vnd.jupyter.widget-state+json": { + "state": { + "68df3ccd903340b4aa8af8aad92c0c20": { + "model_module": "anywidget", + "model_module_version": "0.9.10", + "model_name": "AnyModel", + "state": { + "_anywidget_id": "ipyaladin.widget.Aladin", + "_css": ".cell-output-ipywidget-background{background:transparent}.jp-OutputArea-output,.aladin-widget{background-color:transparent}.aladin-widget .aladin-measurement-div{max-height:100px}\n/*# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vanMvd2lkZ2V0LmNzcyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsiLmNlbGwtb3V0cHV0LWlweXdpZGdldC1iYWNrZ3JvdW5kIHtcbiAgYmFja2dyb3VuZDogdHJhbnNwYXJlbnQ7XG59XG4uanAtT3V0cHV0QXJlYS1vdXRwdXQge1xuICBiYWNrZ3JvdW5kLWNvbG9yOiB0cmFuc3BhcmVudDtcbn1cbi5hbGFkaW4td2lkZ2V0IHtcbiAgYmFja2dyb3VuZC1jb2xvcjogdHJhbnNwYXJlbnQ7XG59XG4uYWxhZGluLXdpZGdldCAuYWxhZGluLW1lYXN1cmVtZW50LWRpdiB7XG4gIG1heC1oZWlnaHQ6IDEwMHB4O1xufVxuIl0sCiAgIm1hcHBpbmdzIjogIkFBQUEsQ0FBQyxpQ0FDQyxXQUFZLFdBQ2QsQ0FDQSxDQUFDLHFCQUdELENBQUMsY0FGQyxpQkFBa0IsV0FDcEIsQ0FJQSxDQUhDLGNBR2MsQ0FBQyx1QkFDZCxXQUFZLEtBQ2QiLAogICJuYW1lcyI6IFtdCn0K */\n", + "_esm": "function f(n){n.charAt(0)===\"_\"&&(n=n.slice(1));let e=n.split(\"_\");for(let a=1;a{this.aladin.gotoRaDec(r,v),console.info(`FITS located at ra: ${r}, dec: ${v}`),URL.revokeObjectURL(o)});this.aladin.setOverlayImageLayer(d,i.name)}handleAddCatalogFromURL(e){let a=h(e.options||{});this.aladin.addCatalog(l.catalogFromURL(e.votable_URL,a))}handleAddMOCFromURL(e){let a=h(e.options||{});this.aladin.addMOC(l.MOCFromURL(e.moc_URL,a))}handleAddMOCFromDict(e){let a=h(e.options||{});this.aladin.addMOC(l.MOCFromJSON(e.moc_dict,a))}handleAddOverlay(e){let a=e.regions_infos,i=h(e.graphic_options||{});i.color||(i.color=\"red\");let s=l.graphicOverlay(i);this.aladin.addOverlay(s);for(let t of a){let o=t.infos;switch(t.region_type){case\"stcs\":s.addFootprints(l.footprintsFromSTCS(o.stcs,t.options));break;case\"circle\":s.add(l.circle(o.ra,o.dec,o.radius,t.options));break;case\"ellipse\":s.add(l.ellipse(o.ra,o.dec,o.a,o.b,o.theta,t.options));break;case\"line\":t.options.lineWidth=t.options.lineWidth||3,s.add(l.vector(o.ra1,o.dec1,o.ra2,o.dec2,t.options));break;case\"polygon\":s.add(l.polygon(o.vertices,t.options));break}}}handleChangeColormap(e){this.aladin.getBaseImageLayer().setColormap(e.colormap)}handleGetJPGThumbnail(){this.aladin.exportAsPNG()}handleTriggerRectangularSelection(){this.aladin.select()}handleAddTable(e,a){let i=h(e.options||{}),s=a[0].buffer,t=new TextDecoder(\"utf-8\"),o=new Blob([t.decode(s)]),d=URL.createObjectURL(o);l.catalogFromURL(d,Object.assign(i,{onClick:\"showTable\"}),r=>{this.aladin.addCatalog(r)},!1),URL.revokeObjectURL(d)}};var p=class{constructor(e,a,i){this.aladin=e,this.aladinDiv=a,this.model=i,this.messageHandler=new g(e,i),this.currentDivNumber=parseInt(a.id.split(\"-\").pop())}isLastDiv(){if(this.currentDivNumber===m)return!0;let e=m;for(let a=e;a>=0;a--){let i=document.getElementById(`aladin-lite-div-${a}`);if(i&&i.style.display!==\"none\"){e=a;break}}return u(e),this.currentDivNumber===e}updateWCS(){this.isLastDiv()&&this.model.set(\"_wcs\",this.aladin.getViewWCS())}update2AxisFoV(){if(!this.isLastDiv())return;let e=this.aladin.getFov();this.model.set(\"_fov_xy\",{x:e[0],y:e[1]})}subscribeAll(){let e=new c,a=new c;this.aladin.on(\"positionChanged\",t=>{if(a.locked){a.unlock();return}e.lock();let o=[t.ra,t.dec];this.updateWCS(),this.model.set(\"_target\",`${o[0]} ${o[1]}`),this.model.save_changes()}),this.model.on(\"change:_target\",()=>{if(e.locked){e.unlock();return}a.lock();let t=this.model.get(\"_target\"),[o,d]=t.split(\" \");this.aladin.gotoRaDec(o,d)});let i=new c,s=new c;this.aladin.on(\"zoomChanged\",t=>{if(s.locked){s.unlock();return}i.lock(),this.updateWCS(),this.update2AxisFoV(),this.model.set(\"_fov\",parseFloat(t.toFixed(5))),this.model.save_changes()}),this.model.on(\"change:_fov\",()=>{if(i.locked){i.unlock();return}s.lock();let t=this.model.get(\"_fov\");this.aladin.setFoV(t)}),this.model.on(\"change:_height\",()=>{let t=this.model.get(\"_height\");this.aladinDiv.style.height=`${t}px`,this.updateWCS(),this.update2AxisFoV(),this.model.save_changes()}),this.aladin.on(\"cooFrameChanged\",()=>{this.updateWCS(),this.model.save_changes()}),this.aladin.on(\"projectionChanged\",()=>{this.updateWCS(),this.model.save_changes()}),this.aladin.on(\"layerChanged\",(t,o,d)=>{o!==\"base\"||d!==\"ADDED\"||(this.updateWCS(),this.model.save_changes())}),this.aladin.on(\"resizeChanged\",()=>{this.updateWCS(),this.update2AxisFoV(),this.model.save_changes()}),this.aladin.on(\"objectHovered\",t=>{t.data!==void 0&&this.model.send({event_type:\"object_hovered\",content:{ra:t.ra,dec:t.dec}})}),this.aladin.on(\"objectClicked\",t=>{if(t){let o={ra:t.ra,dec:t.dec};t.data!==void 0&&(o.data=t.data),this.model.set(\"clicked_object\",o),this.model.send({event_type:\"object_clicked\",content:o}),this.model.save_changes()}}),this.aladin.on(\"click\",t=>{this.model.send({event_type:\"click\",content:t})}),this.aladin.on(\"select\",t=>{let o=[];t.forEach(d=>{d.forEach(r=>{o.push({ra:r.ra,dec:r.dec,data:r.data,x:r.x,y:r.y})})}),this.model.send({event_type:\"select\",content:o})}),this.model.on(\"change:coo_frame\",()=>{this.aladin.setFrame(this.model.get(\"coo_frame\"))}),this.model.on(\"change:survey\",()=>{this.aladin.setImageSurvey(this.model.get(\"survey\"))}),this.model.on(\"change:overlay_survey\",()=>{this.aladin.setOverlayImageLayer(this.model.get(\"overlay_survey\"))}),this.model.on(\"change:overlay_survey_opacity\",()=>{this.aladin.getOverlayImageLayer().setAlpha(this.model.get(\"overlay_survey_opacity\"))}),this.eventHandlers={change_fov:this.messageHandler.handleChangeFoV,goto_ra_dec:this.messageHandler.handleGotoRaDec,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_rectangular_selection:this.messageHandler.handleTriggerRectangularSelection,add_table:this.messageHandler.handleAddTable},this.model.on(\"msg:custom\",(t,o)=>{let d=t.event_name,r=this.eventHandlers[d];if(r)r.call(this,t,o);else throw new Error(`Unknown event name: ${d}`)})}unsubscribeAll(){this.model.off(\"change:_target\"),this.model.off(\"change:_fov\"),this.model.off(\"change:_height\"),this.model.off(\"change:coo_frame\"),this.model.off(\"change:survey\"),this.model.off(\"change:overlay_survey\"),this.model.off(\"change:overlay_survey_opacity\"),this.model.off(\"change:trigger_event\"),this.model.off(\"msg:custom\")}};function b(n,e){u(m+1);let a={};n.get(\"init_options\").forEach(d=>{a[f(d)]=n.get(d)});let i=document.createElement(\"div\");i.classList.add(\"aladin-widget\"),i.style.height=`${a.height}px`,i.id=`aladin-lite-div-${m}`;let s=new l.aladin(i,a),t=a.target.split(\" \");s.gotoRaDec(t[0],t[1]);let o=s.getFov();return n.set(\"_fov_xy\",{x:o[0],y:o[1]}),n.set(\"_wcs\",s.getViewWCS()),n.save_changes(),e.appendChild(i),{aladin:s,aladinDiv:i}}async function C({model:n}){await l.init}function k({model:n,el:e}){let{aladin:a,aladinDiv:i}=b(n,e),s=new p(a,i,n);return s.subscribeAll(),()=>{s.unsubscribeAll()}}var j={initialize:C,render:k};export{j as default};\n//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiLi4vLi4vLi4vanMvdXRpbHMuanMiLCAiLi4vLi4vLi4vanMvYWxhZGluX2xpdGUuanMiLCAiLi4vLi4vLi4vanMvbW9kZWxzL21lc3NhZ2VfaGFuZGxlci5qcyIsICIuLi8uLi8uLi9qcy9tb2RlbHMvZXZlbnRfaGFuZGxlci5qcyIsICIuLi8uLi8uLi9qcy93aWRnZXQuanMiXSwKICAic291cmNlc0NvbnRlbnQiOiBbIi8qKlxuICogQ29udmVydHMgYSBzdHJpbmcgZnJvbSBjYW1lbENhc2UgdG8gc25ha2VfY2FzZS5cbiAqIEBwYXJhbSB7c3RyaW5nfSBzbmFrZUNhc2VTdHIgLSBUaGUgc3RyaW5nIHRvIGNvbnZlcnQuXG4gKiBAcmV0dXJucyB7c3RyaW5nfSBUaGUgc3RyaW5nIGNvbnZlcnRlZCB0byBzbmFrZV9jYXNlLlxuICovXG5mdW5jdGlvbiBzbmFrZUNhc2VUb0NhbWVsQ2FzZShzbmFrZUNhc2VTdHIpIHtcbiAgaWYgKHNuYWtlQ2FzZVN0ci5jaGFyQXQoMCkgPT09IFwiX1wiKSBzbmFrZUNhc2VTdHIgPSBzbmFrZUNhc2VTdHIuc2xpY2UoMSk7XG4gIGxldCB0ZW1wID0gc25ha2VDYXNlU3RyLnNwbGl0KFwiX1wiKTtcbiAgZm9yIChsZXQgaSA9IDE7IGkgPCB0ZW1wLmxlbmd0aDsgaSsrKVxuICAgIHRlbXBbaV0gPSB0ZW1wW2ldLmNoYXJBdCgwKS50b1VwcGVyQ2FzZSgpICsgdGVtcFtpXS5zbGljZSgxKTtcbiAgcmV0dXJuIHRlbXAuam9pbihcIlwiKTtcbn1cblxuLyoqXG4gKiBDb252ZXJ0cyBvcHRpb24gbmFtZXMgaW4gYW4gb2JqZWN0IGZyb20gc25ha2VfY2FzZSB0byBjYW1lbENhc2UuXG4gKiBAcGFyYW0ge09iamVjdH0gb3B0aW9ucyAtIFRoZSBvcHRpb25zIG9iamVjdCB3aXRoIHNuYWtlX2Nhc2UgcHJvcGVydHkgbmFtZXMuXG4gKiBAcmV0dXJucyB7T2JqZWN0fSBBbiBvYmplY3Qgd2l0aCBwcm9wZXJ0eSBuYW1lcyBjb252ZXJ0ZWQgdG8gY2FtZWxDYXNlLlxuICovXG5mdW5jdGlvbiBjb252ZXJ0T3B0aW9uTmFtZXNUb0NhbWVsQ2FzZShvcHRpb25zKSB7XG4gIGNvbnN0IG5ld09wdGlvbnMgPSB7fTtcbiAgZm9yIChjb25zdCBvcHRpb25OYW1lIGluIG9wdGlvbnMpXG4gICAgbmV3T3B0aW9uc1tzbmFrZUNhc2VUb0NhbWVsQ2FzZShvcHRpb25OYW1lKV0gPSBvcHRpb25zW29wdGlvbk5hbWVdO1xuICByZXR1cm4gbmV3T3B0aW9ucztcbn1cblxuY2xhc3MgTG9jayB7XG4gIGxvY2tlZCA9IGZhbHNlO1xuXG4gIC8qKlxuICAgKiBVbmxvY2tzIHRoZSBvYmplY3RcbiAgICovXG4gIHVubG9jaygpIHtcbiAgICB0aGlzLmxvY2tlZCA9IGZhbHNlO1xuICB9XG5cbiAgLyoqXG4gICAqIExvY2tzIHRoZSBvYmplY3RcbiAgICovXG4gIGxvY2soKSB7XG4gICAgdGhpcy5sb2NrZWQgPSB0cnVlO1xuICB9XG59XG5cbmxldCBkaXZOdW1iZXIgPSAtMTtcbmZ1bmN0aW9uIHNldERpdk51bWJlcihudW0pIHtcbiAgZGl2TnVtYmVyID0gbnVtO1xufVxuXG5leHBvcnQge1xuICBzbmFrZUNhc2VUb0NhbWVsQ2FzZSxcbiAgY29udmVydE9wdGlvbk5hbWVzVG9DYW1lbENhc2UsXG4gIExvY2ssXG4gIGRpdk51bWJlcixcbiAgc2V0RGl2TnVtYmVyLFxufTtcbiIsICJpbXBvcnQgQSBmcm9tIFwiaHR0cHM6Ly9lc20uc2gvYWxhZGluLWxpdGVAMy40LjUtYmV0YVwiO1xuXG5leHBvcnQgZGVmYXVsdCBBO1xuIiwgImltcG9ydCB7IGNvbnZlcnRPcHRpb25OYW1lc1RvQ2FtZWxDYXNlIH0gZnJvbSBcIi4uL3V0aWxzXCI7XG5pbXBvcnQgQSBmcm9tIFwiLi4vYWxhZGluX2xpdGVcIjtcblxubGV0IGltYWdlQ291bnQgPSAwO1xuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBNZXNzYWdlSGFuZGxlciB7XG4gIGNvbnN0cnVjdG9yKGFsYWRpbiwgbW9kZWwpIHtcbiAgICB0aGlzLmFsYWRpbiA9IGFsYWRpbjtcbiAgICB0aGlzLm1vZGVsID0gbW9kZWw7XG4gIH1cblxuICBoYW5kbGVDaGFuZ2VGb1YobXNnKSB7XG4gICAgdGhpcy5hbGFkaW4uc2V0Rm9WKG1zZ1tcImZvdlwiXSk7XG4gIH1cblxuICBoYW5kbGVHb3RvUmFEZWMobXNnKSB7XG4gICAgdGhpcy5hbGFkaW4uZ290b1JhRGVjKG1zZ1tcInJhXCJdLCBtc2dbXCJkZWNcIl0pO1xuICB9XG5cbiAgaGFuZGxlQWRkRml0cyhtc2csIGJ1ZmZlcnMpIHtcbiAgICBjb25zdCBvcHRpb25zID0gY29udmVydE9wdGlvbk5hbWVzVG9DYW1lbENhc2UobXNnW1wib3B0aW9uc1wiXSB8fCB7fSk7XG4gICAgaWYgKCFvcHRpb25zLm5hbWUpXG4gICAgICBvcHRpb25zLm5hbWUgPSBgaW1hZ2VfJHtTdHJpbmcoKytpbWFnZUNvdW50KS5wYWRTdGFydCgzLCBcIjBcIil9YDtcbiAgICBjb25zdCBidWZmZXIgPSBidWZmZXJzWzBdO1xuICAgIGNvbnN0IGJsb2IgPSBuZXcgQmxvYihbYnVmZmVyXSwgeyB0eXBlOiBcImFwcGxpY2F0aW9uL29jdGV0LXN0cmVhbVwiIH0pO1xuICAgIGNvbnN0IHVybCA9IFVSTC5jcmVhdGVPYmplY3RVUkwoYmxvYik7XG4gICAgY29uc3QgaW1hZ2UgPSB0aGlzLmFsYWRpbi5jcmVhdGVJbWFnZUZJVFModXJsLCBvcHRpb25zLCAocmEsIGRlYykgPT4ge1xuICAgICAgdGhpcy5hbGFkaW4uZ290b1JhRGVjKHJhLCBkZWMpO1xuICAgICAgY29uc29sZS5pbmZvKGBGSVRTIGxvY2F0ZWQgYXQgcmE6ICR7cmF9LCBkZWM6ICR7ZGVjfWApO1xuICAgICAgVVJMLnJldm9rZU9iamVjdFVSTCh1cmwpO1xuICAgIH0pO1xuICAgIHRoaXMuYWxhZGluLnNldE92ZXJsYXlJbWFnZUxheWVyKGltYWdlLCBvcHRpb25zLm5hbWUpO1xuICB9XG5cbiAgaGFuZGxlQWRkQ2F0YWxvZ0Zyb21VUkwobXNnKSB7XG4gICAgY29uc3Qgb3B0aW9ucyA9IGNvbnZlcnRPcHRpb25OYW1lc1RvQ2FtZWxDYXNlKG1zZ1tcIm9wdGlvbnNcIl0gfHwge30pO1xuICAgIHRoaXMuYWxhZGluLmFkZENhdGFsb2coQS5jYXRhbG9nRnJvbVVSTChtc2dbXCJ2b3RhYmxlX1VSTFwiXSwgb3B0aW9ucykpO1xuICB9XG5cbiAgaGFuZGxlQWRkTU9DRnJvbVVSTChtc2cpIHtcbiAgICBjb25zdCBvcHRpb25zID0gY29udmVydE9wdGlvbk5hbWVzVG9DYW1lbENhc2UobXNnW1wib3B0aW9uc1wiXSB8fCB7fSk7XG4gICAgdGhpcy5hbGFkaW4uYWRkTU9DKEEuTU9DRnJvbVVSTChtc2dbXCJtb2NfVVJMXCJdLCBvcHRpb25zKSk7XG4gIH1cblxuICBoYW5kbGVBZGRNT0NGcm9tRGljdChtc2cpIHtcbiAgICBjb25zdCBvcHRpb25zID0gY29udmVydE9wdGlvbk5hbWVzVG9DYW1lbENhc2UobXNnW1wib3B0aW9uc1wiXSB8fCB7fSk7XG4gICAgdGhpcy5hbGFkaW4uYWRkTU9DKEEuTU9DRnJvbUpTT04obXNnW1wibW9jX2RpY3RcIl0sIG9wdGlvbnMpKTtcbiAgfVxuXG4gIGhhbmRsZUFkZE92ZXJsYXkobXNnKSB7XG4gICAgY29uc3QgcmVnaW9ucyA9IG1zZ1tcInJlZ2lvbnNfaW5mb3NcIl07XG4gICAgY29uc3QgZ3JhcGhpY19vcHRpb25zID0gY29udmVydE9wdGlvbk5hbWVzVG9DYW1lbENhc2UoXG4gICAgICBtc2dbXCJncmFwaGljX29wdGlvbnNcIl0gfHwge30sXG4gICAgKTtcbiAgICBpZiAoIWdyYXBoaWNfb3B0aW9uc1tcImNvbG9yXCJdKSBncmFwaGljX29wdGlvbnNbXCJjb2xvclwiXSA9IFwicmVkXCI7XG4gICAgY29uc3Qgb3ZlcmxheSA9IEEuZ3JhcGhpY092ZXJsYXkoZ3JhcGhpY19vcHRpb25zKTtcbiAgICB0aGlzLmFsYWRpbi5hZGRPdmVybGF5KG92ZXJsYXkpO1xuICAgIGZvciAoY29uc3QgcmVnaW9uIG9mIHJlZ2lvbnMpIHtcbiAgICAgIGNvbnN0IGluZm9zID0gcmVnaW9uW1wiaW5mb3NcIl07XG4gICAgICBzd2l0Y2ggKHJlZ2lvbltcInJlZ2lvbl90eXBlXCJdKSB7XG4gICAgICAgIGNhc2UgXCJzdGNzXCI6XG4gICAgICAgICAgb3ZlcmxheS5hZGRGb290cHJpbnRzKFxuICAgICAgICAgICAgQS5mb290cHJpbnRzRnJvbVNUQ1MoaW5mb3Muc3RjcywgcmVnaW9uLm9wdGlvbnMpLFxuICAgICAgICAgICk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgXCJjaXJjbGVcIjpcbiAgICAgICAgICBvdmVybGF5LmFkZChcbiAgICAgICAgICAgIEEuY2lyY2xlKGluZm9zLnJhLCBpbmZvcy5kZWMsIGluZm9zLnJhZGl1cywgcmVnaW9uLm9wdGlvbnMpLFxuICAgICAgICAgICk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgXCJlbGxpcHNlXCI6XG4gICAgICAgICAgb3ZlcmxheS5hZGQoXG4gICAgICAgICAgICBBLmVsbGlwc2UoXG4gICAgICAgICAgICAgIGluZm9zLnJhLFxuICAgICAgICAgICAgICBpbmZvcy5kZWMsXG4gICAgICAgICAgICAgIGluZm9zLmEsXG4gICAgICAgICAgICAgIGluZm9zLmIsXG4gICAgICAgICAgICAgIGluZm9zLnRoZXRhLFxuICAgICAgICAgICAgICByZWdpb24ub3B0aW9ucyxcbiAgICAgICAgICAgICksXG4gICAgICAgICAgKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSBcImxpbmVcIjpcbiAgICAgICAgICAvLyByZW1vdmUgZGVmYXVsdCBsaW5lV2lkdGggd2hlbiB3ZSBzd2l0Y2ggdG8gQUwgPiAzLjQuNFxuICAgICAgICAgIHJlZ2lvbi5vcHRpb25zLmxpbmVXaWR0aCA9IHJlZ2lvbi5vcHRpb25zLmxpbmVXaWR0aCB8fCAzO1xuICAgICAgICAgIG92ZXJsYXkuYWRkKFxuICAgICAgICAgICAgQS52ZWN0b3IoXG4gICAgICAgICAgICAgIGluZm9zLnJhMSxcbiAgICAgICAgICAgICAgaW5mb3MuZGVjMSxcbiAgICAgICAgICAgICAgaW5mb3MucmEyLFxuICAgICAgICAgICAgICBpbmZvcy5kZWMyLFxuICAgICAgICAgICAgICByZWdpb24ub3B0aW9ucyxcbiAgICAgICAgICAgICksXG4gICAgICAgICAgKTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgY2FzZSBcInBvbHlnb25cIjpcbiAgICAgICAgICBvdmVybGF5LmFkZChBLnBvbHlnb24oaW5mb3MudmVydGljZXMsIHJlZ2lvbi5vcHRpb25zKSk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgaGFuZGxlQ2hhbmdlQ29sb3JtYXAobXNnKSB7XG4gICAgdGhpcy5hbGFkaW4uZ2V0QmFzZUltYWdlTGF5ZXIoKS5zZXRDb2xvcm1hcChtc2dbXCJjb2xvcm1hcFwiXSk7XG4gIH1cblxuICBoYW5kbGVHZXRKUEdUaHVtYm5haWwoKSB7XG4gICAgdGhpcy5hbGFkaW4uZXhwb3J0QXNQTkcoKTtcbiAgfVxuXG4gIGhhbmRsZVRyaWdnZXJSZWN0YW5ndWxhclNlbGVjdGlvbigpIHtcbiAgICB0aGlzLmFsYWRpbi5zZWxlY3QoKTtcbiAgfVxuXG4gIGhhbmRsZUFkZFRhYmxlKG1zZywgYnVmZmVycykge1xuICAgIGNvbnN0IG9wdGlvbnMgPSBjb252ZXJ0T3B0aW9uTmFtZXNUb0NhbWVsQ2FzZShtc2dbXCJvcHRpb25zXCJdIHx8IHt9KTtcbiAgICBjb25zdCBidWZmZXIgPSBidWZmZXJzWzBdLmJ1ZmZlcjtcbiAgICBjb25zdCBkZWNvZGVyID0gbmV3IFRleHREZWNvZGVyKFwidXRmLThcIik7XG4gICAgY29uc3QgYmxvYiA9IG5ldyBCbG9iKFtkZWNvZGVyLmRlY29kZShidWZmZXIpXSk7XG4gICAgY29uc3QgdXJsID0gVVJMLmNyZWF0ZU9iamVjdFVSTChibG9iKTtcbiAgICBBLmNhdGFsb2dGcm9tVVJMKFxuICAgICAgdXJsLFxuICAgICAgT2JqZWN0LmFzc2lnbihvcHRpb25zLCB7IG9uQ2xpY2s6IFwic2hvd1RhYmxlXCIgfSksXG4gICAgICAoY2F0YWxvZykgPT4ge1xuICAgICAgICB0aGlzLmFsYWRpbi5hZGRDYXRhbG9nKGNhdGFsb2cpO1xuICAgICAgfSxcbiAgICAgIGZhbHNlLFxuICAgICk7XG4gICAgVVJMLnJldm9rZU9iamVjdFVSTCh1cmwpO1xuICB9XG59XG4iLCAiaW1wb3J0IE1lc3NhZ2VIYW5kbGVyIGZyb20gXCIuL21lc3NhZ2VfaGFuZGxlclwiO1xuaW1wb3J0IHsgZGl2TnVtYmVyLCBzZXREaXZOdW1iZXIsIExvY2sgfSBmcm9tIFwiLi4vdXRpbHNcIjtcblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgRXZlbnRIYW5kbGVyIHtcbiAgLyoqXG4gICAqIENvbnN0cnVjdG9yIGZvciB0aGUgRXZlbnRIYW5kbGVyIGNsYXNzLlxuICAgKiBAcGFyYW0gYWxhZGluIFRoZSBBbGFkaW4gaW5zdGFuY2VcbiAgICogQHBhcmFtIGFsYWRpbkRpdiBUaGUgQWxhZGluIGRpdlxuICAgKiBAcGFyYW0gbW9kZWwgVGhlIG1vZGVsIGluc3RhbmNlXG4gICAqL1xuICBjb25zdHJ1Y3RvcihhbGFkaW4sIGFsYWRpbkRpdiwgbW9kZWwpIHtcbiAgICB0aGlzLmFsYWRpbiA9IGFsYWRpbjtcbiAgICB0aGlzLmFsYWRpbkRpdiA9IGFsYWRpbkRpdjtcbiAgICB0aGlzLm1vZGVsID0gbW9kZWw7XG4gICAgdGhpcy5tZXNzYWdlSGFuZGxlciA9IG5ldyBNZXNzYWdlSGFuZGxlcihhbGFkaW4sIG1vZGVsKTtcbiAgICB0aGlzLmN1cnJlbnREaXZOdW1iZXIgPSBwYXJzZUludChhbGFkaW5EaXYuaWQuc3BsaXQoXCItXCIpLnBvcCgpKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVja3MgaWYgdGhlIGN1cnJlbnQgZGl2IGlzIHRoZSBsYXN0IGFjdGl2ZSBkaXYuXG4gICAqIEByZXR1cm5zIHtib29sZWFufVxuICAgKi9cbiAgaXNMYXN0RGl2KCkge1xuICAgIGlmICh0aGlzLmN1cnJlbnREaXZOdW1iZXIgPT09IGRpdk51bWJlcikge1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuICAgIGxldCBtYXhEaXYgPSBkaXZOdW1iZXI7XG4gICAgZm9yIChsZXQgaSA9IG1heERpdjsgaSA+PSAwOyBpLS0pIHtcbiAgICAgIGNvbnN0IGFsRGl2ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoYGFsYWRpbi1saXRlLWRpdi0ke2l9YCk7XG4gICAgICBpZiAoIWFsRGl2KSBjb250aW51ZTtcbiAgICAgIGlmIChhbERpdi5zdHlsZS5kaXNwbGF5ICE9PSBcIm5vbmVcIikge1xuICAgICAgICBtYXhEaXYgPSBpO1xuICAgICAgICBicmVhaztcbiAgICAgIH1cbiAgICB9XG4gICAgc2V0RGl2TnVtYmVyKG1heERpdik7XG4gICAgcmV0dXJuIHRoaXMuY3VycmVudERpdk51bWJlciA9PT0gbWF4RGl2O1xuICB9XG5cbiAgLyoqXG4gICAqIFVwZGF0ZXMgdGhlIFdDUyBjb29yZGluYXRlcyBpbiB0aGUgbW9kZWwuXG4gICAqIFdBUk5JTkc6IFRoaXMgbWV0aG9kIGRvbid0IGNhbGwgbW9kZWwuc2F2ZV9jaGFuZ2VzKCkhXG4gICAqL1xuICB1cGRhdGVXQ1MoKSB7XG4gICAgaWYgKCF0aGlzLmlzTGFzdERpdigpKSByZXR1cm47XG4gICAgdGhpcy5tb2RlbC5zZXQoXCJfd2NzXCIsIHRoaXMuYWxhZGluLmdldFZpZXdXQ1MoKSk7XG4gIH1cblxuICAvKipcbiAgICogVXBkYXRlcyB0aGUgMi1heGlzIEZvViBpbiB0aGUgbW9kZWwuXG4gICAqIFdBUk5JTkc6IFRoaXMgbWV0aG9kIGRvbid0IGNhbGwgbW9kZWwuc2F2ZV9jaGFuZ2VzKCkhXG4gICAqL1xuICB1cGRhdGUyQXhpc0ZvVigpIHtcbiAgICBpZiAoIXRoaXMuaXNMYXN0RGl2KCkpIHJldHVybjtcbiAgICBjb25zdCB0d29BeGlzRm9WID0gdGhpcy5hbGFkaW4uZ2V0Rm92KCk7XG4gICAgdGhpcy5tb2RlbC5zZXQoXCJfZm92X3h5XCIsIHtcbiAgICAgIHg6IHR3b0F4aXNGb1ZbMF0sXG4gICAgICB5OiB0d29BeGlzRm9WWzFdLFxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFN1YnNjcmliZXMgdG8gYWxsIHRoZSBldmVudHMgbmVlZGVkIGZvciB0aGUgQWxhZGluIExpdGUgd2lkZ2V0LlxuICAgKi9cbiAgc3Vic2NyaWJlQWxsKCkge1xuICAgIC8qIC0tLS0tLS0tLS0tLS0tLS0tLS0gKi9cbiAgICAvKiBMaXN0ZW5lcnMgLS0tLS0tLS0tICovXG4gICAgLyogLS0tLS0tLS0tLS0tLS0tLS0tLSAqL1xuXG4gICAgLyogUG9zaXRpb24gQ29udHJvbCAqL1xuICAgIC8vIHRoZXJlIGFyZSB0d28gd2F5cyBvZiBjaGFuZ2luZyB0aGUgdGFyZ2V0LCBvbmUgZnJvbSB0aGUgamF2YXNjcmlwdCBzaWRlLCBhbmRcbiAgICAvLyBvbmUgZnJvbSB0aGUgcHl0aG9uIHNpZGUuIFdlIGhhdmUgdG8gaW5zdGFudGlhdGUgdHdvIGxpc3RlbmVycyBmb3IgdGhlc2UsIGJ1dFxuICAgIC8vIHRoZSBnb3RvT2JqZWN0IGNhbGwgc2hvdWxkIG9ubHkgaGFwcGVuIG9uY2UuIFRoZSB0d28gYm9vbGVhbnMgcHJldmVudCB0aGUgdHdvXG4gICAgLy8gbGlzdGVuZXJzIGZyb20gdHJpZ2dlcmluZyBlYWNoIG90aGVyIGFuZCBjcmVhdGluZyBhIGJ1Z2d5IGxvb3AuIFRoZSBzYW1lIHRyaWNrXG4gICAgLy8gaXMgYWxzbyBuZWNlc3NhcnkgZm9yIHRoZSBmaWVsZCBvZiB2aWV3LlxuXG4gICAgLyogVGFyZ2V0IGNvbnRyb2wgKi9cbiAgICBjb25zdCBqc1RhcmdldExvY2sgPSBuZXcgTG9jaygpO1xuICAgIGNvbnN0IHB5VGFyZ2V0TG9jayA9IG5ldyBMb2NrKCk7XG5cbiAgICAvLyBFdmVudCB0cmlnZ2VyZWQgd2hlbiB0aGUgdXNlciBtb3ZlcyB0aGUgbWFwIGluIEFsYWRpbiBMaXRlXG4gICAgdGhpcy5hbGFkaW4ub24oXCJwb3NpdGlvbkNoYW5nZWRcIiwgKHBvc2l0aW9uKSA9PiB7XG4gICAgICBpZiAocHlUYXJnZXRMb2NrLmxvY2tlZCkge1xuICAgICAgICBweVRhcmdldExvY2sudW5sb2NrKCk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGpzVGFyZ2V0TG9jay5sb2NrKCk7XG4gICAgICBjb25zdCByYURlYyA9IFtwb3NpdGlvbi5yYSwgcG9zaXRpb24uZGVjXTtcbiAgICAgIHRoaXMudXBkYXRlV0NTKCk7XG4gICAgICB0aGlzLm1vZGVsLnNldChcIl90YXJnZXRcIiwgYCR7cmFEZWNbMF19ICR7cmFEZWNbMV19YCk7XG4gICAgICB0aGlzLm1vZGVsLnNhdmVfY2hhbmdlcygpO1xuICAgIH0pO1xuXG4gICAgdGhpcy5tb2RlbC5vbihcImNoYW5nZTpfdGFyZ2V0XCIsICgpID0+IHtcbiAgICAgIGlmIChqc1RhcmdldExvY2subG9ja2VkKSB7XG4gICAgICAgIGpzVGFyZ2V0TG9jay51bmxvY2soKTtcbiAgICAgICAgcmV0dXJuO1xuICAgICAgfVxuICAgICAgcHlUYXJnZXRMb2NrLmxvY2soKTtcbiAgICAgIGxldCB0YXJnZXQgPSB0aGlzLm1vZGVsLmdldChcIl90YXJnZXRcIik7XG4gICAgICBjb25zdCBbcmEsIGRlY10gPSB0YXJnZXQuc3BsaXQoXCIgXCIpO1xuICAgICAgdGhpcy5hbGFkaW4uZ290b1JhRGVjKHJhLCBkZWMpO1xuICAgIH0pO1xuXG4gICAgLyogRmllbGQgb2YgVmlldyBjb250cm9sICovXG4gICAgY29uc3QganNGb3ZMb2NrID0gbmV3IExvY2soKTtcbiAgICBjb25zdCBweUZvdkxvY2sgPSBuZXcgTG9jaygpO1xuXG4gICAgdGhpcy5hbGFkaW4ub24oXCJ6b29tQ2hhbmdlZFwiLCAoZm92KSA9PiB7XG4gICAgICBpZiAocHlGb3ZMb2NrLmxvY2tlZCkge1xuICAgICAgICBweUZvdkxvY2sudW5sb2NrKCk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIGpzRm92TG9jay5sb2NrKCk7XG4gICAgICAvLyBmb3YgTVVTVCBiZSBjYXN0IGludG8gZmxvYXQgaW4gb3JkZXIgdG8gYmUgc2VudCB0byB0aGUgbW9kZWxcbiAgICAgIHRoaXMudXBkYXRlV0NTKCk7XG4gICAgICB0aGlzLnVwZGF0ZTJBeGlzRm9WKCk7XG4gICAgICB0aGlzLm1vZGVsLnNldChcIl9mb3ZcIiwgcGFyc2VGbG9hdChmb3YudG9GaXhlZCg1KSkpO1xuICAgICAgdGhpcy5tb2RlbC5zYXZlX2NoYW5nZXMoKTtcbiAgICB9KTtcblxuICAgIHRoaXMubW9kZWwub24oXCJjaGFuZ2U6X2ZvdlwiLCAoKSA9PiB7XG4gICAgICBpZiAoanNGb3ZMb2NrLmxvY2tlZCkge1xuICAgICAgICBqc0ZvdkxvY2sudW5sb2NrKCk7XG4gICAgICAgIHJldHVybjtcbiAgICAgIH1cbiAgICAgIHB5Rm92TG9jay5sb2NrKCk7XG4gICAgICBsZXQgZm92ID0gdGhpcy5tb2RlbC5nZXQoXCJfZm92XCIpO1xuICAgICAgdGhpcy5hbGFkaW4uc2V0Rm9WKGZvdik7XG4gICAgfSk7XG5cbiAgICAvKiBEaXYgY29udHJvbCAqL1xuICAgIHRoaXMubW9kZWwub24oXCJjaGFuZ2U6X2hlaWdodFwiLCAoKSA9PiB7XG4gICAgICBsZXQgaGVpZ2h0ID0gdGhpcy5tb2RlbC5nZXQoXCJfaGVpZ2h0XCIpO1xuICAgICAgdGhpcy5hbGFkaW5EaXYuc3R5bGUuaGVpZ2h0ID0gYCR7aGVpZ2h0fXB4YDtcbiAgICAgIC8vIFVwZGF0ZSBXQ1MgYW5kIEZvViBvbmx5IGlmIHRoaXMgaXMgdGhlIGxhc3QgZGl2XG4gICAgICB0aGlzLnVwZGF0ZVdDUygpO1xuICAgICAgdGhpcy51cGRhdGUyQXhpc0ZvVigpO1xuICAgICAgdGhpcy5tb2RlbC5zYXZlX2NoYW5nZXMoKTtcbiAgICB9KTtcblxuICAgIC8qIEFsYWRpbiBjYWxsYmFja3MgKi9cblxuICAgIHRoaXMuYWxhZGluLm9uKFwiY29vRnJhbWVDaGFuZ2VkXCIsICgpID0+IHtcbiAgICAgIHRoaXMudXBkYXRlV0NTKCk7XG4gICAgICB0aGlzLm1vZGVsLnNhdmVfY2hhbmdlcygpO1xuICAgIH0pO1xuXG4gICAgdGhpcy5hbGFkaW4ub24oXCJwcm9qZWN0aW9uQ2hhbmdlZFwiLCAoKSA9PiB7XG4gICAgICB0aGlzLnVwZGF0ZVdDUygpO1xuICAgICAgdGhpcy5tb2RlbC5zYXZlX2NoYW5nZXMoKTtcbiAgICB9KTtcblxuICAgIHRoaXMuYWxhZGluLm9uKFwibGF5ZXJDaGFuZ2VkXCIsIChfLCBsYXllck5hbWUsIHN0YXRlKSA9PiB7XG4gICAgICBpZiAobGF5ZXJOYW1lICE9PSBcImJhc2VcIiB8fCBzdGF0ZSAhPT0gXCJBRERFRFwiKSByZXR1cm47XG4gICAgICB0aGlzLnVwZGF0ZVdDUygpO1xuICAgICAgdGhpcy5tb2RlbC5zYXZlX2NoYW5nZXMoKTtcbiAgICB9KTtcblxuICAgIHRoaXMuYWxhZGluLm9uKFwicmVzaXplQ2hhbmdlZFwiLCAoKSA9PiB7XG4gICAgICB0aGlzLnVwZGF0ZVdDUygpO1xuICAgICAgdGhpcy51cGRhdGUyQXhpc0ZvVigpO1xuICAgICAgdGhpcy5tb2RlbC5zYXZlX2NoYW5nZXMoKTtcbiAgICB9KTtcblxuICAgIHRoaXMuYWxhZGluLm9uKFwib2JqZWN0SG92ZXJlZFwiLCAob2JqZWN0KSA9PiB7XG4gICAgICBpZiAob2JqZWN0W1wiZGF0YVwiXSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHRoaXMubW9kZWwuc2VuZCh7XG4gICAgICAgICAgZXZlbnRfdHlwZTogXCJvYmplY3RfaG92ZXJlZFwiLFxuICAgICAgICAgIGNvbnRlbnQ6IHtcbiAgICAgICAgICAgIHJhOiBvYmplY3RbXCJyYVwiXSxcbiAgICAgICAgICAgIGRlYzogb2JqZWN0W1wiZGVjXCJdLFxuICAgICAgICAgIH0sXG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgIH0pO1xuXG4gICAgdGhpcy5hbGFkaW4ub24oXCJvYmplY3RDbGlja2VkXCIsIChjbGlja2VkKSA9PiB7XG4gICAgICBpZiAoY2xpY2tlZCkge1xuICAgICAgICBsZXQgY2xpY2tlZENvbnRlbnQgPSB7XG4gICAgICAgICAgcmE6IGNsaWNrZWRbXCJyYVwiXSxcbiAgICAgICAgICBkZWM6IGNsaWNrZWRbXCJkZWNcIl0sXG4gICAgICAgIH07XG4gICAgICAgIGlmIChjbGlja2VkW1wiZGF0YVwiXSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgY2xpY2tlZENvbnRlbnRbXCJkYXRhXCJdID0gY2xpY2tlZFtcImRhdGFcIl07XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5tb2RlbC5zZXQoXCJjbGlja2VkX29iamVjdFwiLCBjbGlja2VkQ29udGVudCk7XG4gICAgICAgIC8vIHNlbmQgYSBjdXN0b20gbWVzc2FnZSBpbiBjYXNlIHRoZSB1c2VyIHdhbnRzIHRvIGRlZmluZSB0aGVpciBvd24gY2FsbGJhY2tzXG4gICAgICAgIHRoaXMubW9kZWwuc2VuZCh7XG4gICAgICAgICAgZXZlbnRfdHlwZTogXCJvYmplY3RfY2xpY2tlZFwiLFxuICAgICAgICAgIGNvbnRlbnQ6IGNsaWNrZWRDb250ZW50LFxuICAgICAgICB9KTtcbiAgICAgICAgdGhpcy5tb2RlbC5zYXZlX2NoYW5nZXMoKTtcbiAgICAgIH1cbiAgICB9KTtcblxuICAgIHRoaXMuYWxhZGluLm9uKFwiY2xpY2tcIiwgKGNsaWNrQ29udGVudCkgPT4ge1xuICAgICAgdGhpcy5tb2RlbC5zZW5kKHtcbiAgICAgICAgZXZlbnRfdHlwZTogXCJjbGlja1wiLFxuICAgICAgICBjb250ZW50OiBjbGlja0NvbnRlbnQsXG4gICAgICB9KTtcbiAgICB9KTtcblxuICAgIHRoaXMuYWxhZGluLm9uKFwic2VsZWN0XCIsIChjYXRhbG9ncykgPT4ge1xuICAgICAgbGV0IG9iamVjdHNEYXRhID0gW107XG4gICAgICAvLyBUT0RPOiB0aGlzIGZsYXR0ZW5zIHRoZSBzZWxlY3Rpb24uIEVhY2ggb2JqZWN0IGZyb20gZGlmZmVyZW50XG4gICAgICAvLyBjYXRhbG9ncyBhcmUgZW50ZXJlZCBpbiB0aGUgYXJyYXkuIFRvIGNoYW5nZSB0aGlzLCBtYXliZSBjaGFuZ2VcbiAgICAgIC8vIHVwc3RyZWFtIHdoYXQgaXMgcmV0dXJuZWQgdXBvbiBzZWxlY3Rpb24/XG4gICAgICBjYXRhbG9ncy5mb3JFYWNoKChjYXRhbG9nKSA9PiB7XG4gICAgICAgIGNhdGFsb2cuZm9yRWFjaCgob2JqZWN0KSA9PiB7XG4gICAgICAgICAgb2JqZWN0c0RhdGEucHVzaCh7XG4gICAgICAgICAgICByYTogb2JqZWN0LnJhLFxuICAgICAgICAgICAgZGVjOiBvYmplY3QuZGVjLFxuICAgICAgICAgICAgZGF0YTogb2JqZWN0LmRhdGEsXG4gICAgICAgICAgICB4OiBvYmplY3QueCxcbiAgICAgICAgICAgIHk6IG9iamVjdC55LFxuICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcbiAgICAgIH0pO1xuICAgICAgdGhpcy5tb2RlbC5zZW5kKHtcbiAgICAgICAgZXZlbnRfdHlwZTogXCJzZWxlY3RcIixcbiAgICAgICAgY29udGVudDogb2JqZWN0c0RhdGEsXG4gICAgICB9KTtcbiAgICB9KTtcblxuICAgIC8qIEFsYWRpbiBmdW5jdGlvbmFsaXRpZXMgKi9cblxuICAgIHRoaXMubW9kZWwub24oXCJjaGFuZ2U6Y29vX2ZyYW1lXCIsICgpID0+IHtcbiAgICAgIHRoaXMuYWxhZGluLnNldEZyYW1lKHRoaXMubW9kZWwuZ2V0KFwiY29vX2ZyYW1lXCIpKTtcbiAgICB9KTtcblxuICAgIHRoaXMubW9kZWwub24oXCJjaGFuZ2U6c3VydmV5XCIsICgpID0+IHtcbiAgICAgIHRoaXMuYWxhZGluLnNldEltYWdlU3VydmV5KHRoaXMubW9kZWwuZ2V0KFwic3VydmV5XCIpKTtcbiAgICB9KTtcblxuICAgIHRoaXMubW9kZWwub24oXCJjaGFuZ2U6b3ZlcmxheV9zdXJ2ZXlcIiwgKCkgPT4ge1xuICAgICAgdGhpcy5hbGFkaW4uc2V0T3ZlcmxheUltYWdlTGF5ZXIodGhpcy5tb2RlbC5nZXQoXCJvdmVybGF5X3N1cnZleVwiKSk7XG4gICAgfSk7XG5cbiAgICB0aGlzLm1vZGVsLm9uKFwiY2hhbmdlOm92ZXJsYXlfc3VydmV5X29wYWNpdHlcIiwgKCkgPT4ge1xuICAgICAgdGhpcy5hbGFkaW5cbiAgICAgICAgLmdldE92ZXJsYXlJbWFnZUxheWVyKClcbiAgICAgICAgLnNldEFscGhhKHRoaXMubW9kZWwuZ2V0KFwib3ZlcmxheV9zdXJ2ZXlfb3BhY2l0eVwiKSk7XG4gICAgfSk7XG5cbiAgICB0aGlzLmV2ZW50SGFuZGxlcnMgPSB7XG4gICAgICBjaGFuZ2VfZm92OiB0aGlzLm1lc3NhZ2VIYW5kbGVyLmhhbmRsZUNoYW5nZUZvVixcbiAgICAgIGdvdG9fcmFfZGVjOiB0aGlzLm1lc3NhZ2VIYW5kbGVyLmhhbmRsZUdvdG9SYURlYyxcbiAgICAgIGFkZF9maXRzOiB0aGlzLm1lc3NhZ2VIYW5kbGVyLmhhbmRsZUFkZEZpdHMsXG4gICAgICBhZGRfY2F0YWxvZ19mcm9tX1VSTDogdGhpcy5tZXNzYWdlSGFuZGxlci5oYW5kbGVBZGRDYXRhbG9nRnJvbVVSTCxcbiAgICAgIGFkZF9NT0NfZnJvbV9VUkw6IHRoaXMubWVzc2FnZUhhbmRsZXIuaGFuZGxlQWRkTU9DRnJvbVVSTCxcbiAgICAgIGFkZF9NT0NfZnJvbV9kaWN0OiB0aGlzLm1lc3NhZ2VIYW5kbGVyLmhhbmRsZUFkZE1PQ0Zyb21EaWN0LFxuICAgICAgYWRkX292ZXJsYXk6IHRoaXMubWVzc2FnZUhhbmRsZXIuaGFuZGxlQWRkT3ZlcmxheSxcbiAgICAgIGNoYW5nZV9jb2xvcm1hcDogdGhpcy5tZXNzYWdlSGFuZGxlci5oYW5kbGVDaGFuZ2VDb2xvcm1hcCxcbiAgICAgIGdldF9KUEdfdGh1bWJuYWlsOiB0aGlzLm1lc3NhZ2VIYW5kbGVyLmhhbmRsZUdldEpQR1RodW1ibmFpbCxcbiAgICAgIHRyaWdnZXJfcmVjdGFuZ3VsYXJfc2VsZWN0aW9uOlxuICAgICAgICB0aGlzLm1lc3NhZ2VIYW5kbGVyLmhhbmRsZVRyaWdnZXJSZWN0YW5ndWxhclNlbGVjdGlvbixcbiAgICAgIGFkZF90YWJsZTogdGhpcy5tZXNzYWdlSGFuZGxlci5oYW5kbGVBZGRUYWJsZSxcbiAgICB9O1xuXG4gICAgdGhpcy5tb2RlbC5vbihcIm1zZzpjdXN0b21cIiwgKG1zZywgYnVmZmVycykgPT4ge1xuICAgICAgY29uc3QgZXZlbnROYW1lID0gbXNnW1wiZXZlbnRfbmFtZVwiXTtcbiAgICAgIGNvbnN0IGhhbmRsZXIgPSB0aGlzLmV2ZW50SGFuZGxlcnNbZXZlbnROYW1lXTtcbiAgICAgIGlmIChoYW5kbGVyKSBoYW5kbGVyLmNhbGwodGhpcywgbXNnLCBidWZmZXJzKTtcbiAgICAgIGVsc2UgdGhyb3cgbmV3IEVycm9yKGBVbmtub3duIGV2ZW50IG5hbWU6ICR7ZXZlbnROYW1lfWApO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFVuc3Vic2NyaWJlIGZyb20gYWxsIHRoZSBtb2RlbCBldmVudHMuXG4gICAqIFRoZXJlIGlzIG5vIG5lZWQgdG8gdW5zdWJzY3JpYmUgZnJvbSB0aGUgQWxhZGluIExpdGUgZXZlbnRzLlxuICAgKi9cbiAgdW5zdWJzY3JpYmVBbGwoKSB7XG4gICAgdGhpcy5tb2RlbC5vZmYoXCJjaGFuZ2U6X3RhcmdldFwiKTtcbiAgICB0aGlzLm1vZGVsLm9mZihcImNoYW5nZTpfZm92XCIpO1xuICAgIHRoaXMubW9kZWwub2ZmKFwiY2hhbmdlOl9oZWlnaHRcIik7XG4gICAgdGhpcy5tb2RlbC5vZmYoXCJjaGFuZ2U6Y29vX2ZyYW1lXCIpO1xuICAgIHRoaXMubW9kZWwub2ZmKFwiY2hhbmdlOnN1cnZleVwiKTtcbiAgICB0aGlzLm1vZGVsLm9mZihcImNoYW5nZTpvdmVybGF5X3N1cnZleVwiKTtcbiAgICB0aGlzLm1vZGVsLm9mZihcImNoYW5nZTpvdmVybGF5X3N1cnZleV9vcGFjaXR5XCIpO1xuICAgIHRoaXMubW9kZWwub2ZmKFwiY2hhbmdlOnRyaWdnZXJfZXZlbnRcIik7XG4gICAgdGhpcy5tb2RlbC5vZmYoXCJtc2c6Y3VzdG9tXCIpO1xuICB9XG59XG4iLCAiaW1wb3J0IFwiLi93aWRnZXQuY3NzXCI7XG5pbXBvcnQgRXZlbnRIYW5kbGVyIGZyb20gXCIuL21vZGVscy9ldmVudF9oYW5kbGVyXCI7XG5pbXBvcnQgeyBkaXZOdW1iZXIsIHNldERpdk51bWJlciwgc25ha2VDYXNlVG9DYW1lbENhc2UgfSBmcm9tIFwiLi91dGlsc1wiO1xuaW1wb3J0IEEgZnJvbSBcIi4vYWxhZGluX2xpdGVcIjtcblxuZnVuY3Rpb24gaW5pdEFsYWRpbkxpdGUobW9kZWwsIGVsKSB7XG4gIHNldERpdk51bWJlcihkaXZOdW1iZXIgKyAxKTtcbiAgbGV0IGluaXRPcHRpb25zID0ge307XG4gIG1vZGVsLmdldChcImluaXRfb3B0aW9uc1wiKS5mb3JFYWNoKChvcHRpb25fbmFtZSkgPT4ge1xuICAgIGluaXRPcHRpb25zW3NuYWtlQ2FzZVRvQ2FtZWxDYXNlKG9wdGlvbl9uYW1lKV0gPSBtb2RlbC5nZXQob3B0aW9uX25hbWUpO1xuICB9KTtcblxuICBsZXQgYWxhZGluRGl2ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcImRpdlwiKTtcbiAgYWxhZGluRGl2LmNsYXNzTGlzdC5hZGQoXCJhbGFkaW4td2lkZ2V0XCIpO1xuICBhbGFkaW5EaXYuc3R5bGUuaGVpZ2h0ID0gYCR7aW5pdE9wdGlvbnNbXCJoZWlnaHRcIl19cHhgO1xuXG4gIGFsYWRpbkRpdi5pZCA9IGBhbGFkaW4tbGl0ZS1kaXYtJHtkaXZOdW1iZXJ9YDtcbiAgbGV0IGFsYWRpbiA9IG5ldyBBLmFsYWRpbihhbGFkaW5EaXYsIGluaXRPcHRpb25zKTtcblxuICAvLyBTZXQgdGhlIHRhcmdldCBhZ2FpbiBhZnRlciB0aGUgaW5pdGlhbGl6YXRpb24gdG8gYmUgc3VyZSB0aGF0IHRoZSB0YXJnZXQgaXMgc2V0XG4gIC8vIGZyb20gaWNycyBjb29yZGluYXRlcyBiZWNhdXNlIG9mIHRoZSB1c2Ugb2YgZ290b09iamVjdCBpbiB0aGUgQWxhZGluIExpdGUgQVBJXG4gIGNvbnN0IHJhRGVjID0gaW5pdE9wdGlvbnNbXCJ0YXJnZXRcIl0uc3BsaXQoXCIgXCIpO1xuICBhbGFkaW4uZ290b1JhRGVjKHJhRGVjWzBdLCByYURlY1sxXSk7XG5cbiAgLy8gU2V0IGN1cnJlbnQgRm9WIGFuZCBXQ1NcbiAgY29uc3QgdHdvQXhpc0ZvViA9IGFsYWRpbi5nZXRGb3YoKTtcbiAgbW9kZWwuc2V0KFwiX2Zvdl94eVwiLCB7XG4gICAgeDogdHdvQXhpc0ZvVlswXSxcbiAgICB5OiB0d29BeGlzRm9WWzFdLFxuICB9KTtcbiAgbW9kZWwuc2V0KFwiX3djc1wiLCBhbGFkaW4uZ2V0Vmlld1dDUygpKTtcbiAgbW9kZWwuc2F2ZV9jaGFuZ2VzKCk7XG5cbiAgZWwuYXBwZW5kQ2hpbGQoYWxhZGluRGl2KTtcbiAgcmV0dXJuIHsgYWxhZGluLCBhbGFkaW5EaXYgfTtcbn1cblxuYXN5bmMgZnVuY3Rpb24gaW5pdGlhbGl6ZSh7IG1vZGVsIH0pIHtcbiAgYXdhaXQgQS5pbml0O1xufVxuXG5mdW5jdGlvbiByZW5kZXIoeyBtb2RlbCwgZWwgfSkge1xuICAvKiAtLS0tLS0tLS0tLS0tLS0tLS0tICovXG4gIC8qIFZpZXcgLS0tLS0tLS0tLS0tLS0gKi9cbiAgLyogLS0tLS0tLS0tLS0tLS0tLS0tLSAqL1xuXG4gIGNvbnN0IHsgYWxhZGluLCBhbGFkaW5EaXYgfSA9IGluaXRBbGFkaW5MaXRlKG1vZGVsLCBlbCk7XG5cbiAgY29uc3QgZXZlbnRIYW5kbGVyID0gbmV3IEV2ZW50SGFuZGxlcihhbGFkaW4sIGFsYWRpbkRpdiwgbW9kZWwpO1xuICBldmVudEhhbmRsZXIuc3Vic2NyaWJlQWxsKCk7XG5cbiAgcmV0dXJuICgpID0+IHtcbiAgICAvLyBOZWVkIHRvIHVuc3Vic2NyaWJlIHRoZSBsaXN0ZW5lcnNcbiAgICBldmVudEhhbmRsZXIudW5zdWJzY3JpYmVBbGwoKTtcbiAgfTtcbn1cblxuZXhwb3J0IGRlZmF1bHQgeyBpbml0aWFsaXplLCByZW5kZXIgfTtcbiJdLAogICJtYXBwaW5ncyI6ICJB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QUFTLGNBQWMsS0FBSyxFQUM1Q0EsRUFBVSxVQUFVLElBQUksZUFBZSxFQUN2Q0EsRUFBVSxNQUFNLE9BQVMsR0FBR0gsRUFBWSxNQUFTLEtBRWpERyxFQUFVLEdBQUssbUJBQW1CSixDQUFTLEdBQzNDLElBQUlLLEVBQVMsSUFBSUMsRUFBRSxPQUFPRixFQUFXSCxDQUFXLEVBSTFDTSxFQUFRTixFQUFZLE9BQVUsTUFBTSxHQUFHLEVBQzdDSSxFQUFPLFVBQVVFLEVBQU0sQ0FBQyxFQUFHQSxFQUFNLENBQUMsQ0FBQyxFQUduQyxJQUFNQyxFQUFhSCxFQUFPLE9BQU8sRUFDakMsT0FBQVIsRUFBTSxJQUFJLFVBQVcsQ0FDbkIsRUFBR1csRUFBVyxDQUFDLEVBQ2YsRUFBR0EsRUFBVyxDQUFDLENBQ2pCLENBQUMsRUFDRFgsRUFBTSxJQUFJLE9BQVFRLEVBQU8sV0FBVyxDQUFDLEVBQ3JDUixFQUFNLGFBQWEsRUFFbkJDLEVBQUcsWUFBWU0sQ0FBUyxFQUNqQixDQUFFLE9BQUFDLEVBQVEsVUFBQUQsQ0FBVSxDQUM3QixDQUVBLGVBQWVLLEVBQVcsQ0FBRSxNQUFBWixDQUFNLEVBQUcsQ0FDbkMsTUFBTVMsRUFBRSxJQUNWLENBRUEsU0FBU0ksRUFBTyxDQUFFLE1BQUFiLEVBQU8sR0FBQUMsQ0FBRyxFQUFHLENBSzdCLEdBQU0sQ0FBRSxPQUFBTyxFQUFRLFVBQUFELENBQVUsRUFBSVIsRUFBZUMsRUFBT0MsQ0FBRSxFQUVoRGEsRUFBZSxJQUFJQyxFQUFhUCxFQUFRRCxFQUFXUCxDQUFLLEVBQzlELE9BQUFjLEVBQWEsYUFBYSxFQUVuQixJQUFNLENBRVhBLEVBQWEsZUFBZSxDQUM5QixDQUNGLENBRUEsSUFBT0UsRUFBUSxDQUFFLFdBQUFKLEVBQVksT0FBQUMsQ0FBTyIsCiAgIm5hbWVzIjogWyJzbmFrZUNhc2VUb0NhbWVsQ2FzZSIsICJzbmFrZUNhc2VTdHIiLCAidGVtcCIsICJpIiwgImNvbnZlcnRPcHRpb25OYW1lc1RvQ2FtZWxDYXNlIiwgIm9wdGlvbnMiLCAibmV3T3B0aW9ucyIsICJvcHRpb25OYW1lIiwgIkxvY2siLCAiZGl2TnVtYmVyIiwgInNldERpdk51bWJlciIsICJudW0iLCAiQSIsICJhbGFkaW5fbGl0ZV9kZWZhdWx0IiwgImltYWdlQ291bnQiLCAiTWVzc2FnZUhhbmRsZXIiLCAiYWxhZGluIiwgIm1vZGVsIiwgIm1zZyIsICJidWZmZXJzIiwgIm9wdGlvbnMiLCAiY29udmVydE9wdGlvbk5hbWVzVG9DYW1lbENhc2UiLCAiYnVmZmVyIiwgImJsb2IiLCAidXJsIiwgImltYWdlIiwgInJhIiwgImRlYyIsICJhbGFkaW5fbGl0ZV9kZWZhdWx0IiwgInJlZ2lvbnMiLCAiZ3JhcGhpY19vcHRpb25zIiwgIm92ZXJsYXkiLCAicmVnaW9uIiwgImluZm9zIiwgImRlY29kZXIiLCAiY2F0YWxvZyIsICJFdmVudEhhbmRsZXIiLCAiYWxhZGluIiwgImFsYWRpbkRpdiIsICJtb2RlbCIsICJNZXNzYWdlSGFuZGxlciIsICJkaXZOdW1iZXIiLCAibWF4RGl2IiwgImkiLCAiYWxEaXYiLCAic2V0RGl2TnVtYmVyIiwgInR3b0F4aXNGb1YiLCAianNUYXJnZXRMb2NrIiwgIkxvY2siLCAicHlUYXJnZXRMb2NrIiwgInBvc2l0aW9uIiwgInJhRGVjIiwgInRhcmdldCIsICJyYSIsICJkZWMiLCAianNGb3ZMb2NrIiwgInB5Rm92TG9jayIsICJmb3YiLCAiaGVpZ2h0IiwgIl8iLCAibGF5ZXJOYW1lIiwgInN0YXRlIiwgIm9iamVjdCIsICJjbGlja2VkIiwgImNsaWNrZWRDb250ZW50IiwgImNsaWNrQ29udGVudCIsICJjYXRhbG9ncyIsICJvYmplY3RzRGF0YSIsICJjYXRhbG9nIiwgIm1zZyIsICJidWZmZXJzIiwgImV2ZW50TmFtZSIsICJoYW5kbGVyIiwgImluaXRBbGFkaW5MaXRlIiwgIm1vZGVsIiwgImVsIiwgInNldERpdk51bWJlciIsICJkaXZOdW1iZXIiLCAiaW5pdE9wdGlvbnMiLCAib3B0aW9uX25hbWUiLCAic25ha2VDYXNlVG9DYW1lbENhc2UiLCAiYWxhZGluRGl2IiwgImFsYWRpbiIsICJhbGFkaW5fbGl0ZV9kZWZhdWx0IiwgInJhRGVjIiwgInR3b0F4aXNGb1YiLCAiaW5pdGlhbGl6ZSIsICJyZW5kZXIiLCAiZXZlbnRIYW5kbGVyIiwgIkV2ZW50SGFuZGxlciIsICJ3aWRnZXRfZGVmYXVsdCJdCn0K\n", + "_fov": 50, + "_fov_xy": { + "x": 5, + "y": 5 + }, + "_height": 800, + "_model_module": "anywidget", + "_model_module_version": "0.9.10", + "_model_name": "AnyModel", + "_target": "80.89416999999995 -69.75611", + "_view_module": "anywidget", + "_view_module_version": "0.9.10", + "_view_name": "AnyView", + "_wcs": { + "CDELT1": -5, + "CDELT2": 5, + "CRPIX1": 1, + "CRPIX2": 1, + "CRVAL1": 80.89416999999995, + "CRVAL2": -69.75611, + "CTYPE1": "RA---SIN", + "CTYPE2": "DEC--SIN", + "CUNIT1": "deg ", + "CUNIT2": "deg ", + "LONPOLE": 180.00000000000006, + "NAXIS": 2, + "NAXIS1": 1, + "NAXIS2": 1, + "RADESYS": "ICRS " + }, + "background_color": "rgb(60, 60, 60)", + "clicked_object": {}, + "coo_frame": "J2000", + "full_screen": false, + "grid_color": "rgb(178, 50, 178)", + "grid_opacity": 0.5, + "grid_options": { + "color": { + "b": 0.6980392156862745, + "g": 0.19607843137254902, + "r": 0.6980392156862745 + }, + "enabled": false, + "labelSize": 15, + "opacity": 0.5, + "showLabels": true, + "thickness": 2 + }, + "init_options": [ + "_fov", + "_height", + "_target", + "background_color", + "coo_frame", + "full_screen", + "grid_color", + "grid_opacity", + "grid_options", + "overlay_survey", + "overlay_survey_opacity", + "projection", + "reticle_color", + "reticle_size", + "samp", + "show_catalog", + "show_context_menu", + "show_coo_grid", + "show_coo_grid_control", + "show_coo_location", + "show_fov", + "show_frame", + "show_fullscreen_control", + "show_layers_control", + "show_overlay_stack_control", + "show_projection_control", + "show_reticle", + "show_settings_control", + "show_share_control", + "show_simbad_pointer_control", + "show_status_bar", + "show_zoom_control", + "survey" + ], + "layout": "IPY_MODEL_8b1f9d5607de4bd4a8cc50de5de97650", + "overlay_survey": "", + "overlay_survey_opacity": 0, + "projection": "SIN", + "reticle_color": "rgb(178, 50, 178)", + "reticle_size": 20, + "samp": false, + "show_catalog": true, + "show_context_menu": true, + "show_coo_grid": false, + "show_coo_grid_control": true, + "show_coo_location": true, + "show_fov": true, + "show_frame": true, + "show_fullscreen_control": true, + "show_layers_control": true, + "show_overlay_stack_control": true, + "show_projection_control": true, + "show_reticle": true, + "show_settings_control": true, + "show_share_control": false, + "show_simbad_pointer_control": true, + "show_status_bar": true, + "show_zoom_control": false, + "survey": "CDS/P/PLANCK/R2/HFI/color" + } + }, + "8b1f9d5607de4bd4a8cc50de5de97650": { + "model_module": "@jupyter-widgets/base", + "model_module_version": "2.0.0", + "model_name": "LayoutModel", + "state": {} + } + }, + "version_major": 2, + "version_minor": 0 + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/js/aladin_lite.js b/js/aladin_lite.js index 379517f1..98addcb1 100644 --- a/js/aladin_lite.js +++ b/js/aladin_lite.js @@ -1,3 +1,3 @@ -import A from "https://esm.sh/aladin-lite@3.4.4-beta"; +import A from "https://esm.sh/aladin-lite@3.4.5-beta"; export default A; diff --git a/js/models/event_handler.js b/js/models/event_handler.js index d3c3bb8c..4c1ccaea 100644 --- a/js/models/event_handler.js +++ b/js/models/event_handler.js @@ -1,5 +1,5 @@ import MessageHandler from "./message_handler"; -import { Lock } from "../utils"; +import { divNumber, setDivNumber, Lock } from "../utils"; export default class EventHandler { /** @@ -12,7 +12,51 @@ export default class EventHandler { this.aladin = aladin; this.aladinDiv = aladinDiv; this.model = model; - this.messageHandler = new MessageHandler(aladin); + this.messageHandler = new MessageHandler(aladin, model); + this.currentDivNumber = parseInt(aladinDiv.id.split("-").pop()); + } + + /** + * Checks if the current div is the last active div. + * @returns {boolean} + */ + isLastDiv() { + if (this.currentDivNumber === divNumber) { + return true; + } + let maxDiv = divNumber; + for (let i = maxDiv; i >= 0; i--) { + const alDiv = document.getElementById(`aladin-lite-div-${i}`); + if (!alDiv) continue; + if (alDiv.style.display !== "none") { + maxDiv = i; + break; + } + } + setDivNumber(maxDiv); + return this.currentDivNumber === maxDiv; + } + + /** + * Updates the WCS coordinates in the model. + * WARNING: This method don't call model.save_changes()! + */ + updateWCS() { + if (!this.isLastDiv()) return; + this.model.set("_wcs", this.aladin.getViewWCS()); + } + + /** + * Updates the 2-axis FoV in the model. + * WARNING: This method don't call model.save_changes()! + */ + update2AxisFoV() { + if (!this.isLastDiv()) return; + const twoAxisFoV = this.aladin.getFov(); + this.model.set("_fov_xy", { + x: twoAxisFoV[0], + y: twoAxisFoV[1], + }); } /** @@ -42,6 +86,7 @@ export default class EventHandler { } jsTargetLock.lock(); const raDec = [position.ra, position.dec]; + this.updateWCS(); this.model.set("_target", `${raDec[0]} ${raDec[1]}`); this.model.save_changes(); }); @@ -68,6 +113,8 @@ export default class EventHandler { } jsFovLock.lock(); // fov MUST be cast into float in order to be sent to the model + this.updateWCS(); + this.update2AxisFoV(); this.model.set("_fov", parseFloat(fov.toFixed(5))); this.model.save_changes(); }); @@ -83,13 +130,39 @@ export default class EventHandler { }); /* Div control */ - this.model.on("change:height", () => { - let height = this.model.get("height"); + this.model.on("change:_height", () => { + let height = this.model.get("_height"); this.aladinDiv.style.height = `${height}px`; + // Update WCS and FoV only if this is the last div + this.updateWCS(); + this.update2AxisFoV(); + this.model.save_changes(); }); /* Aladin callbacks */ + this.aladin.on("cooFrameChanged", () => { + this.updateWCS(); + this.model.save_changes(); + }); + + this.aladin.on("projectionChanged", () => { + this.updateWCS(); + this.model.save_changes(); + }); + + this.aladin.on("layerChanged", (_, layerName, state) => { + if (layerName !== "base" || state !== "ADDED") return; + this.updateWCS(); + this.model.save_changes(); + }); + + this.aladin.on("resizeChanged", () => { + this.updateWCS(); + this.update2AxisFoV(); + this.model.save_changes(); + }); + this.aladin.on("objectHovered", (object) => { if (object["data"] !== undefined) { this.model.send({ @@ -200,7 +273,7 @@ export default class EventHandler { unsubscribeAll() { this.model.off("change:_target"); this.model.off("change:_fov"); - this.model.off("change:height"); + this.model.off("change:_height"); this.model.off("change:coo_frame"); this.model.off("change:survey"); this.model.off("change:overlay_survey"); diff --git a/js/models/message_handler.js b/js/models/message_handler.js index b5387d7f..76fd9a25 100644 --- a/js/models/message_handler.js +++ b/js/models/message_handler.js @@ -4,8 +4,9 @@ import A from "../aladin_lite"; let imageCount = 0; export default class MessageHandler { - constructor(aladin) { + constructor(aladin, model) { this.aladin = aladin; + this.model = model; } handleChangeFoV(msg) { diff --git a/js/utils.js b/js/utils.js index 41cc3660..d2ac7295 100644 --- a/js/utils.js +++ b/js/utils.js @@ -41,4 +41,15 @@ class Lock { } } -export { snakeCaseToCamelCase, convertOptionNamesToCamelCase, Lock }; +let divNumber = -1; +function setDivNumber(num) { + divNumber = num; +} + +export { + snakeCaseToCamelCase, + convertOptionNamesToCamelCase, + Lock, + divNumber, + setDivNumber, +}; diff --git a/js/widget.js b/js/widget.js index 7b261dde..b2e13fc0 100644 --- a/js/widget.js +++ b/js/widget.js @@ -1,11 +1,10 @@ import "./widget.css"; import EventHandler from "./models/event_handler"; -import { snakeCaseToCamelCase } from "./utils"; +import { divNumber, setDivNumber, snakeCaseToCamelCase } from "./utils"; import A from "./aladin_lite"; -let idxView = 0; - function initAladinLite(model, el) { + setDivNumber(divNumber + 1); let initOptions = {}; model.get("init_options").forEach((option_name) => { initOptions[snakeCaseToCamelCase(option_name)] = model.get(option_name); @@ -15,15 +14,23 @@ function initAladinLite(model, el) { aladinDiv.classList.add("aladin-widget"); aladinDiv.style.height = `${initOptions["height"]}px`; - aladinDiv.id = `aladin-lite-div-${idxView}`; + aladinDiv.id = `aladin-lite-div-${divNumber}`; let aladin = new A.aladin(aladinDiv, initOptions); - idxView += 1; // Set the target again after the initialization to be sure that the target is set // from icrs coordinates because of the use of gotoObject in the Aladin Lite API const raDec = initOptions["target"].split(" "); aladin.gotoRaDec(raDec[0], raDec[1]); + // Set current FoV and WCS + const twoAxisFoV = aladin.getFov(); + model.set("_fov_xy", { + x: twoAxisFoV[0], + y: twoAxisFoV[1], + }); + model.set("_wcs", aladin.getViewWCS()); + model.save_changes(); + el.appendChild(aladinDiv); return { aladin, aladinDiv }; } diff --git a/src/ipyaladin/utils/__init__.py b/src/ipyaladin/utils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/ipyaladin/coordinate_parser.py b/src/ipyaladin/utils/coordinate_parser.py similarity index 100% rename from src/ipyaladin/coordinate_parser.py rename to src/ipyaladin/utils/coordinate_parser.py diff --git a/src/ipyaladin/utils/exceptions.py b/src/ipyaladin/utils/exceptions.py new file mode 100644 index 00000000..a77b3e73 --- /dev/null +++ b/src/ipyaladin/utils/exceptions.py @@ -0,0 +1,6 @@ +class WidgetCommunicationError(OSError): + """Error raised when there is a communication error with the widget.""" + + def __init__(self, message: str) -> None: + self.message = message + super(WidgetCommunicationError, self).__init__(message) diff --git a/src/ipyaladin/region_converter.py b/src/ipyaladin/utils/region_converter.py similarity index 100% rename from src/ipyaladin/region_converter.py rename to src/ipyaladin/utils/region_converter.py diff --git a/src/ipyaladin/widget.py b/src/ipyaladin/widget.py index 735fee5a..9f528bd9 100644 --- a/src/ipyaladin/widget.py +++ b/src/ipyaladin/widget.py @@ -8,18 +8,22 @@ import io import pathlib from pathlib import Path -import typing -from typing import ClassVar, Union, Final, Optional +from typing import ClassVar, Dict, Final, List, Optional, Tuple, Union import warnings import anywidget +from astropy.coordinates import SkyCoord, Angle from astropy.table.table import QTable from astropy.table import Table -from astropy.coordinates import SkyCoord, Angle from astropy.io import fits as astropy_fits from astropy.io.fits import HDUList +from astropy.wcs import WCS +import numpy as np import traitlets +from .utils.exceptions import WidgetCommunicationError +from .utils.coordinate_parser import parse_coordinate_string + try: from regions import ( CircleSkyRegion, @@ -47,10 +51,8 @@ default, ) -from .coordinate_parser import parse_coordinate_string - SupportedRegion = Union[ - typing.List[ + List[ Union[ CircleSkyRegion, EllipseSkyRegion, @@ -79,7 +81,7 @@ class Aladin(anywidget.AnyWidget): _css: Final = pathlib.Path(__file__).parent / "static" / "widget.css" # Options for the view initialization - height = Int(400).tag(sync=True, init_option=True) + _height = Int(400).tag(sync=True, init_option=True) _target = Unicode( "0 0", help="A private trait that stores the current target of the widget in a string." @@ -126,10 +128,14 @@ class Aladin(anywidget.AnyWidget): grid_opacity = Float(0.5).tag(sync=True, init_option=True) grid_options = traitlets.Dict().tag(sync=True, init_option=True) + # Values + _wcs = traitlets.Dict().tag(sync=True) + _fov_xy = traitlets.Dict().tag(sync=True) + # content of the last click clicked_object = traitlets.Dict().tag(sync=True) # listener callback is on the python side and contains functions to link to events - listener_callback: ClassVar[typing.Dict[str, callable]] = {} + listener_callback: ClassVar[Dict[str, callable]] = {} # overlay survey overlay_survey = Unicode("").tag(sync=True, init_option=True) @@ -138,11 +144,12 @@ class Aladin(anywidget.AnyWidget): init_options = traitlets.List(trait=Any()).tag(sync=True) @default("init_options") - def _init_options(self) -> typing.List[str]: + def _init_options(self) -> List[str]: return list(self.traits(init_option=True)) def __init__(self, *args: any, **kwargs: any) -> None: super().__init__(*args, **kwargs) + self.height = kwargs.get("height", 400) self.target = kwargs.get("target", "0 0") self.fov = kwargs.get("fov", 60.0) self.on_msg(self._handle_custom_message) @@ -165,6 +172,65 @@ def _handle_custom_message(self, _: any, message: dict, __: any) -> None: elif event_type == "select" and "select" in self.listener_callback: self.listener_callback["select"](message_content) + @property + def height(self) -> int: + """The height of the Aladin Lite widget. + + Returns + ------- + int + The height of the widget in pixels. + + """ + return self._height + + @height.setter + def height(self, height: int) -> None: + if np.isclose(self._height, height): + return + self._wcs = {} + self._fov_xy = {} + self._height = height + + @property + def wcs(self) -> WCS: + """The world coordinate system of the Aladin Lite widget. + + Returns + ------- + WCS + An astropy WCS object representing the world coordinate system. + + """ + if self._wcs == {}: + raise WidgetCommunicationError( + "The world coordinate system is not available. " + "Please recover it from another cell." + ) + if "RADECSYS" in self._wcs: # RADECSYS keyword is deprecated for astropy.WCS + self._wcs["RADESYS"] = self._wcs.pop("RADECSYS") + return WCS(self._wcs) + + @property + def fov_xy(self) -> Tuple[Angle, Angle]: + """The field of view of the Aladin Lite along the two axes. + + Returns + ------- + tuple[Angle, Angle] + A tuple of astropy.units.Angle objects representing the field of view. + + """ + if self._fov_xy == {}: + raise WidgetCommunicationError( + "The field of view along the two axes is not available. " + "Please recover it from another cell." + ) + return ( + Angle(self._fov_xy["x"], unit="deg"), + Angle(self._fov_xy["y"], unit="deg"), + ) + @property def fov(self) -> Angle: """The field of view of the Aladin Lite widget along the horizontal axis. @@ -185,6 +251,10 @@ def fov(self, fov: Union[float, Angle]) -> None: if isinstance(fov, Angle): fov = fov.deg self._fov = fov + if np.isclose(fov, self._fov): + return + self._fov_xy = {} + self._wcs = {} self.send({"event_name": "change_fov", "fov": fov}) @property @@ -216,6 +286,7 @@ def target(self, target: Union[str, SkyCoord]) -> None: "target must be a string or an astropy.coordinates.SkyCoord object" ) self._target = f"{target.icrs.ra.deg} {target.icrs.dec.deg}" + self._wcs = {} self.send( { "event_name": "goto_ra_dec", @@ -266,6 +337,7 @@ def add_fits(self, fits: Union[str, Path, HDUList], **image_options: any) -> Non fits_bytes = io.BytesIO() fits.writeto(fits_bytes) + self._wcs = {} self.send( {"event_name": "add_fits", "options": image_options}, buffers=[fits_bytes.getvalue()], @@ -463,7 +535,7 @@ def add_graphic_overlay_from_region( "See the documentation for the supported region types." ) - from .region_converter import RegionInfos + from .utils.region_converter import RegionInfos # Define behavior for each region type regions_infos.append(RegionInfos(region_element).to_clean_dict()) @@ -477,7 +549,7 @@ def add_graphic_overlay_from_region( ) def add_overlay_from_stcs( - self, stc_string: Union[typing.List[str], str], **overlay_options: any + self, stc_string: Union[List[str], str], **overlay_options: any ) -> None: """Add an overlay layer defined by an STC-S string. @@ -498,7 +570,7 @@ def add_overlay_from_stcs( self.add_graphic_overlay_from_stcs(stc_string, **overlay_options) def add_graphic_overlay_from_stcs( - self, stc_string: Union[typing.List[str], str], **overlay_options: any + self, stc_string: Union[List[str], str], **overlay_options: any ) -> None: """Add an overlay layer defined by an STC-S string. diff --git a/src/test/test_aladin.py b/src/test/test_aladin.py index 4ff155cf..14a42cac 100644 --- a/src/test/test_aladin.py +++ b/src/test/test_aladin.py @@ -4,7 +4,7 @@ from typing import Callable from ipyaladin import Aladin -from ipyaladin.coordinate_parser import parse_coordinate_string +from ipyaladin.utils.coordinate_parser import parse_coordinate_string from .test_coordinate_parser import test_is_coordinate_string_values diff --git a/src/test/test_coordinate_parser.py b/src/test/test_coordinate_parser.py index 9282f2b0..bcd5d3f1 100644 --- a/src/test/test_coordinate_parser.py +++ b/src/test/test_coordinate_parser.py @@ -1,5 +1,5 @@ from typing import Tuple -from ipyaladin.coordinate_parser import ( +from ipyaladin.utils.coordinate_parser import ( parse_coordinate_string, _split_coordinate_string, _is_hour_angle_string,