cubevis 0.5.22__tar.gz → 0.5.24__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of cubevis might be problematic. Click here for more details.

Files changed (150) hide show
  1. {cubevis-0.5.22 → cubevis-0.5.24}/PKG-INFO +1 -1
  2. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/toolbox/_cube.py +2 -2
  3. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/toolbox/_interactive_clean_ui.mustache +213 -72
  4. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/toolbox/_interactive_clean_ui.py +213 -72
  5. {cubevis-0.5.22 → cubevis-0.5.24}/pyproject.toml +1 -1
  6. {cubevis-0.5.22 → cubevis-0.5.24}/LICENSE +0 -0
  7. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/LICENSE.rst +0 -0
  8. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__icons__/20px/fast-backward.svg +0 -0
  9. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__icons__/20px/fast-forward.svg +0 -0
  10. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__icons__/20px/step-backward.svg +0 -0
  11. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__icons__/20px/step-forward.svg +0 -0
  12. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__icons__/add-chan.png +0 -0
  13. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__icons__/add-chan.svg +0 -0
  14. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__icons__/add-cube.png +0 -0
  15. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__icons__/add-cube.svg +0 -0
  16. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__icons__/drag.png +0 -0
  17. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__icons__/drag.svg +0 -0
  18. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__icons__/mask-selected.png +0 -0
  19. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__icons__/mask.png +0 -0
  20. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__icons__/mask.svg +0 -0
  21. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__icons__/new-layer-sm-selected.png +0 -0
  22. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__icons__/new-layer-sm-selected.svg +0 -0
  23. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__icons__/new-layer-sm.png +0 -0
  24. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__icons__/new-layer-sm.svg +0 -0
  25. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__icons__/reset.png +0 -0
  26. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__icons__/reset.svg +0 -0
  27. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__icons__/sub-chan.png +0 -0
  28. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__icons__/sub-chan.svg +0 -0
  29. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__icons__/sub-cube.png +0 -0
  30. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__icons__/sub-cube.svg +0 -0
  31. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__icons__/zoom-to-fit.png +0 -0
  32. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__icons__/zoom-to-fit.svg +0 -0
  33. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__init__.py +0 -0
  34. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__js__/bokeh-3.6.1.min.js +0 -0
  35. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__js__/bokeh-tables-3.6.1.min.js +0 -0
  36. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__js__/bokeh-widgets-3.6.1.min.js +0 -0
  37. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__js__/casalib.min.js +0 -0
  38. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/__js__/cubevisjs.min.js +0 -0
  39. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/__init__.py +0 -0
  40. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/annotations/__init__.py +0 -0
  41. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/annotations/_ev_poly_annotation.py +0 -0
  42. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/components/__init__.py +0 -0
  43. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/format/__init__.py +0 -0
  44. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/format/_time_ticks.py +0 -0
  45. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/format/_wcs_ticks.py +0 -0
  46. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/models/__init__.py +0 -0
  47. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/models/_edit_span.py +0 -0
  48. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/models/_ev_text_input.py +0 -0
  49. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/models/_tip.py +0 -0
  50. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/models/_tip_button.py +0 -0
  51. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/sources/__init__.py +0 -0
  52. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/sources/_data_pipe.py +0 -0
  53. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/sources/_image_data_source.py +0 -0
  54. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/sources/_image_pipe.py +0 -0
  55. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/sources/_spectra_data_source.py +0 -0
  56. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/sources/_updatable_data_source.py +0 -0
  57. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/state/__init__.py +0 -0
  58. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/state/_initialize.py +0 -0
  59. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/state/_javascript.py +0 -0
  60. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/state/_palette.py +0 -0
  61. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/state/_session.py +0 -0
  62. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/state/js/bokeh-2.4.1.min.js +0 -0
  63. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/state/js/bokeh-gl-2.4.1.min.js +0 -0
  64. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/state/js/bokeh-tables-2.4.1.min.js +0 -0
  65. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/state/js/bokeh-widgets-2.4.1.min.js +0 -0
  66. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/state/js/casaguijs-v0.0.4.0-b2.4.min.js +0 -0
  67. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/state/js/casaguijs-v0.0.5.0-b2.4.min.js +0 -0
  68. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/state/js/casaguijs-v0.0.6.0-b2.4.min.js +0 -0
  69. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/state/js/casalib-v0.0.1.min.js +0 -0
  70. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/tools/__init__.py +0 -0
  71. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/tools/_cbreset_tool.py +0 -0
  72. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/tools/_drag_tool.py +0 -0
  73. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/utils/__init__.py +0 -0
  74. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/utils/_axes_labels.py +0 -0
  75. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/bokeh/utils/_svg_icon.py +0 -0
  76. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/data/__init__.py +0 -0
  77. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/data/casaimage/__init__.py +0 -0
  78. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/exe/__init__.py +0 -0
  79. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/exe/_context.py +0 -0
  80. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/exe/_mode.py +0 -0
  81. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/exe/_setting.py +0 -0
  82. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/exe/_task.py +0 -0
  83. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/private/_gclean.py +0 -0
  84. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/private/apps/__init__.py +0 -0
  85. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/private/apps/_createmask.py +0 -0
  86. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/private/apps/_createregion.py +0 -0
  87. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/private/apps/_interactiveclean.mustache +0 -0
  88. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/private/apps/_interactiveclean.py +0 -0
  89. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/private/apps/_plotants.py +0 -0
  90. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/private/apps/_plotbandpass.py +0 -0
  91. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/private/casashell/createmask.py +0 -0
  92. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/private/casashell/iclean.py +0 -0
  93. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/private/casatasks/__init__.py +0 -0
  94. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/private/casatasks/createmask.py +0 -0
  95. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/private/casatasks/createregion.py +0 -0
  96. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/private/casatasks/iclean.py +0 -0
  97. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/readme.rst +0 -0
  98. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/remote/__init__.py +0 -0
  99. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/remote/_gclean.py +0 -0
  100. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/remote/_local.py +0 -0
  101. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/remote/_remote_kernel.py +0 -0
  102. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/toolbox/__init__.py +0 -0
  103. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/toolbox/_app_context.py +0 -0
  104. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/toolbox/_interactiveclean_wrappers.py +0 -0
  105. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/toolbox/_region_list.py +0 -0
  106. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/utils/_ResourceManager.py +0 -0
  107. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/utils/__init__.py +0 -0
  108. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/utils/_browser.py +0 -0
  109. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/utils/_contextmgrchain.py +0 -0
  110. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/utils/_conversion.py +0 -0
  111. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/utils/_copydoc.py +0 -0
  112. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/utils/_docenum.py +0 -0
  113. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/utils/_import_protected_module.py +0 -0
  114. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/utils/_jupyter.py +0 -0
  115. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/utils/_logging.py +0 -0
  116. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/utils/_pkgs.py +0 -0
  117. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/utils/_regions.py +0 -0
  118. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/utils/_static.py +0 -0
  119. {cubevis-0.5.22 → cubevis-0.5.24}/cubevis/utils/_tiles.py +0 -0
  120. {cubevis-0.5.22 → cubevis-0.5.24}/readme.rst +0 -0
  121. {cubevis-0.5.22 → cubevis-0.5.24}/tests/manual/alma-many-chan/alma-many-chan.py +0 -0
  122. {cubevis-0.5.22 → cubevis-0.5.24}/tests/manual/basic-websockets-demo/client.html +0 -0
  123. {cubevis-0.5.22 → cubevis-0.5.24}/tests/manual/basic-websockets-demo/client.py +0 -0
  124. {cubevis-0.5.22 → cubevis-0.5.24}/tests/manual/basic-websockets-demo/server.py +0 -0
  125. {cubevis-0.5.22 → cubevis-0.5.24}/tests/manual/createmask-demo/run-createmask.py +0 -0
  126. {cubevis-0.5.22 → cubevis-0.5.24}/tests/manual/createregion-demo/run-createregion.py +0 -0
  127. {cubevis-0.5.22 → cubevis-0.5.24}/tests/manual/cubemask-demo/image-slider-spectra-done-stats.py +0 -0
  128. {cubevis-0.5.22 → cubevis-0.5.24}/tests/manual/cubemask-demo/image-slider-spectra-done.py +0 -0
  129. {cubevis-0.5.22 → cubevis-0.5.24}/tests/manual/cubemask-demo/image-slider-spectra.py +0 -0
  130. {cubevis-0.5.22 → cubevis-0.5.24}/tests/manual/cubemask-demo/image-slider.py +0 -0
  131. {cubevis-0.5.22 → cubevis-0.5.24}/tests/manual/cubemask-demo/image.py +0 -0
  132. {cubevis-0.5.22 → cubevis-0.5.24}/tests/manual/iclean-demo/m100_interactive.py +0 -0
  133. {cubevis-0.5.22 → cubevis-0.5.24}/tests/manual/iclean-demo/mask0-iclean.py +0 -0
  134. {cubevis-0.5.22 → cubevis-0.5.24}/tests/manual/iclean-demo/run-gclean.py +0 -0
  135. {cubevis-0.5.22 → cubevis-0.5.24}/tests/manual/iclean-demo/run-iclean-obj.py +0 -0
  136. {cubevis-0.5.22 → cubevis-0.5.24}/tests/manual/iclean-demo/run-iclean.py +0 -0
  137. {cubevis-0.5.22 → cubevis-0.5.24}/tests/manual/iclean-demo/vla-sim-jet-iclean.py +0 -0
  138. {cubevis-0.5.22 → cubevis-0.5.24}/tests/manual/iclean-first-look/run-fl-cont.py +0 -0
  139. {cubevis-0.5.22 → cubevis-0.5.24}/tests/manual/iclean-first-look/run-fl-line.py +0 -0
  140. {cubevis-0.5.22 → cubevis-0.5.24}/tests/manual/iclean-outlier/run-iclean.py +0 -0
  141. {cubevis-0.5.22 → cubevis-0.5.24}/tests/manual/iclean-outlier/test_outlier.txt +0 -0
  142. {cubevis-0.5.22 → cubevis-0.5.24}/tests/manual/iclean-remote/iclean_remote_webserver.py +0 -0
  143. {cubevis-0.5.22 → cubevis-0.5.24}/tests/manual/large-cube/run-largecube.py +0 -0
  144. {cubevis-0.5.22 → cubevis-0.5.24}/tests/manual/svg-test.py +0 -0
  145. {cubevis-0.5.22 → cubevis-0.5.24}/tests/manual/updatable-data-source/direct-plot.py +0 -0
  146. {cubevis-0.5.22 → cubevis-0.5.24}/tests/manual/updatable-data-source/simple-update.py +0 -0
  147. {cubevis-0.5.22 → cubevis-0.5.24}/tests/manual/updatable-data-source/updated-plot.py +0 -0
  148. {cubevis-0.5.22 → cubevis-0.5.24}/tests/manual/uranus-demo/uranus-iclean.py +0 -0
  149. {cubevis-0.5.22 → cubevis-0.5.24}/tests/manual/websocket-reconnect/client.html +0 -0
  150. {cubevis-0.5.22 → cubevis-0.5.24}/tests/manual/websocket-reconnect/server.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cubevis
3
- Version: 0.5.22
3
+ Version: 0.5.24
4
4
  Summary: visualization toolkit and apps for casa
5
5
  License: LGPL
6
6
  Author-email: Darrell Schiebel <darrell@schiebel.us>,Pam Harris <pharris@nrao.edu>
@@ -880,7 +880,7 @@ class CubeMask:
880
880
  ### square: 𝑦=𝑥^2
881
881
  ### gamma: 𝑦=𝑥^𝛾
882
882
  ### power: 𝑦=(𝛼^𝑥−1)/(𝛼−1)
883
- self._cm_adjust['alpha-value'] = TextInput( value="1000", prefix="alpha", max_width=170, visible=False )
883
+ self._cm_adjust['alpha-value'] = TextInput( value="10", prefix="alpha", max_width=170, visible=False )
884
884
  self._cm_adjust['gamma-value'] = TextInput( value="1", prefix="gamma", max_width=170, visible=False )
885
885
  self._cm_adjust['equation'] = Div(text='''<math><mrow><mi>y</mi><mo>=</mo><mi>x</mi></mrow></math>''', margin=(12, 0, 0, 0)) # linear
886
886
  self._cm_adjust['scaling'] = Dropdown( label='linear',
@@ -2969,8 +2969,8 @@ class CubeMask:
2969
2969
  ( spec ) => {
2970
2970
  if ( isource.masking_on( ) )
2971
2971
  refresh_pixel_display( spec.index,
2972
+ spec.spectrum.pixel[spec.chan[1]],
2972
2973
  'mask' in spec ? spec.mask[spec.chan[1]] : undefined,
2973
- 'mask' in spec && spec.mask[spec.chan[1]],
2974
2974
  pix_wrld && pix_wrld.label == 'pixel' ? false : true )
2975
2975
  } )
2976
2976
  if ( go_to && ! go_to._has_focus ) {
@@ -45,12 +45,13 @@ import asyncio
45
45
  import shutil
46
46
  import websockets
47
47
  from os.path import basename, abspath, exists, join
48
+ import numpy as np
48
49
  from uuid import uuid4
49
50
  from html import escape as html_escape
50
51
  from contextlib import asynccontextmanager
51
52
  from bokeh.models import Button, TextInput, Checkbox, Div, LinearAxis, CustomJS, Spacer, Span, HoverTool, DataRange1d, Step, InlineStyleSheet
52
53
  from bokeh.events import ModelEvent, MouseEnter
53
- from bokeh.models import TabPanel, Tabs
54
+ from bokeh.models import TabPanel, Tabs, Range1d
54
55
  from bokeh.plotting import ColumnDataSource, figure, show
55
56
  from bokeh.layouts import column, row, layout
56
57
  from bokeh.io import reset_output as reset_bokeh_output, output_notebook
@@ -343,57 +344,159 @@ class InteractiveCleanUI:
343
344
  </div>'''
344
345
 
345
346
  hover = HoverTool( tooltips=TOOLTIPS )
346
- imdetails['gui']['convergence'] = figure( sizing_mode=sizing_mode, y_axis_location="right",
347
- tools=[ hover ], toolbar_location=None, **kw )
348
347
 
349
- if orient == 'vertical':
350
- imdetails['gui']['convergence'].yaxis.axis_label='Iteration (cycle threshold dotted red)'
351
- imdetails['gui']['convergence'].xaxis.axis_label='Peak Residual'
352
- imdetails['gui']['convergence'].extra_x_ranges = { 'residual_range': DataRange1d( follow='end' ),
353
- 'flux_range': DataRange1d( follow='end' ) }
354
-
355
- imdetails['gui']['convergence'].step( 'values', 'iterations', source=imdetails['converge-data']['cyclethreshold'],
356
- line_color='red', x_range_name='residual_range', line_dash='dotted', line_width=2 )
357
- imdetails['gui']['convergence'].line( 'values', 'iterations', source=imdetails['converge-data']['residual'],
358
- line_color=self._converge_color['residual'], x_range_name='residual_range' )
359
- imdetails['gui']['convergence'].scatter( 'values', 'iterations', source=imdetails['converge-data']['residual'],
360
- color=self._converge_color['residual'], x_range_name='residual_range',size=10 )
361
- imdetails['gui']['convergence'].line( 'values', 'iterations', source=imdetails['converge-data']['flux'],
362
- line_color=self._converge_color['flux'], x_range_name='flux_range' )
363
- imdetails['gui']['convergence'].scatter( 'values', 'iterations', source=imdetails['converge-data']['flux'],
364
- color=self._converge_color['flux'], x_range_name='flux_range', size=10 )
365
-
366
- imdetails['gui']['convergence'].add_layout( LinearAxis( x_range_name='flux_range', axis_label='Total Flux',
367
- axis_line_color=self._converge_color['flux'],
368
- major_label_text_color=self._converge_color['flux'],
369
- axis_label_text_color=self._converge_color['flux'],
370
- major_tick_line_color=self._converge_color['flux'],
371
- minor_tick_line_color=self._converge_color['flux'] ), 'above')
348
+ ###
349
+ ### Data source that will be used for updating the convergence plot
350
+ ###
351
+ stokes = 0
352
+ convergence = imdetails['converge']['chan'][0][stokes]
353
+ imdetails['converge-data'] = { }
354
+ imdetails['converge-data']['flux'] = ColumnDataSource( data=dict( values=convergence['modelFlux'], iterations=convergence['iterations'],
355
+ cyclethreshold=convergence['cycleThresh'],
356
+ stopDesc=list( map( ImagingDict.get_summaryminor_stopdesc, convergence['stopCode'] ) ),
357
+ type=['flux'] * len(convergence['iterations']) ) )
358
+ imdetails['converge-data']['residual'] = ColumnDataSource( data=dict( values=convergence['peakRes'], iterations=convergence['iterations'],
359
+ cyclethreshold=convergence['cycleThresh'],
360
+ stopDesc=list( map( ImagingDict.get_summaryminor_stopdesc, convergence['stopCode'] ) ),
361
+ type=['residual'] * len(convergence['iterations'])) )
362
+ imdetails['converge-data']['cyclethreshold'] = ColumnDataSource( data=dict( values=convergence['cycleThresh'], iterations=convergence['iterations'] ) )
363
+
364
+ # Calculate explicit ranges for each dataset
365
+ flux_values = convergence['modelFlux']
366
+ residual_values = convergence['peakRes']
367
+ iterations = convergence['iterations']
368
+ cyclethresh_values = convergence['cycleThresh']
369
+
370
+ # Calculate ranges with padding
371
+ if len(flux_values) > 0:
372
+ flux_min, flux_max = np.min(flux_values), np.max(flux_values)
373
+ flux_padding = max((flux_max - flux_min) * 0.1, abs(flux_max * 0.05)) if flux_max != flux_min else abs(flux_max * 0.1)
374
+ else:
375
+ flux_min, flux_max, flux_padding = 0, 1, 0.1
372
376
 
377
+ if len(residual_values) > 0:
378
+ residual_min, residual_max = np.min(residual_values), np.max(residual_values)
379
+ residual_padding = max((residual_max - residual_min) * 0.1, abs(residual_max * 0.05)) if residual_max != residual_min else abs(residual_max * 0.1)
373
380
  else:
374
- imdetails['gui']['convergence'].xaxis.axis_label='Iteration (cycle threshold dotted red)'
375
- imdetails['gui']['convergence'].yaxis.axis_label='Peak Residual'
376
- imdetails['gui']['convergence'].extra_y_ranges = { 'residual_range': DataRange1d( follow='end' ),
377
- 'flux_range': DataRange1d( follow='end' ) }
378
-
379
- imdetails['gui']['convergence'].step( 'iterations', 'values', source=imdetails['converge-data']['cyclethreshold'],
380
- line_color='red', y_range_name='residual_range', line_dash='dotted', line_width=2 )
381
- imdetails['gui']['convergence'].line( 'iterations', 'values', source=imdetails['converge-data']['residual'],
382
- line_color=self._converge_color['residual'], y_range_name='residual_range' )
383
- imdetails['gui']['convergence'].scatter( 'iterations', 'values', source=imdetails['converge-data']['residual'],
384
- color=self._converge_color['residual'], y_range_name='residual_range',size=10 )
385
- imdetails['gui']['convergence'].line( 'iterations', 'values', source=imdetails['converge-data']['flux'],
386
- line_color=self._converge_color['flux'], y_range_name='flux_range' )
387
- imdetails['gui']['convergence'].scatter( 'iterations', 'values', source=imdetails['converge-data']['flux'],
388
- color=self._converge_color['flux'], y_range_name='flux_range', size=10 )
389
-
390
- imdetails['gui']['convergence'].add_layout( LinearAxis( y_range_name='flux_range', axis_label='Total Flux',
391
- axis_line_color=self._converge_color['flux'],
392
- major_label_text_color=self._converge_color['flux'],
393
- axis_label_text_color=self._converge_color['flux'],
394
- major_tick_line_color=self._converge_color['flux'],
395
- minor_tick_line_color=self._converge_color['flux'] ), 'right')
381
+ residual_min, residual_max, residual_padding = 0, 1, 0.1
382
+
383
+ # Create Range1d objects
384
+ flux_range = Range1d(start=flux_min - flux_padding, end=flux_max + flux_padding)
385
+ residual_range = Range1d(start=residual_min - residual_padding, end=residual_max + residual_padding)
386
+
387
+ # Ensure ranges are valid (non-zero span)
388
+ if flux_range.end - flux_range.start < 1e-10:
389
+ center = flux_range.start
390
+ if abs(center) < 1e-10: # If center is essentially 0
391
+ flux_range.start = -0.1
392
+ flux_range.end = 1.0
393
+ else:
394
+ span = max(abs(center * 0.2), 0.1)
395
+ flux_range.start = center - span
396
+ flux_range.end = center + span
397
+
398
+ if residual_range.end - residual_range.start < 1e-10:
399
+ center = residual_range.start
400
+ span = max(abs(center * 0.2), 0.1)
401
+ residual_range.start = center - span
402
+ residual_range.end = center + span
403
+
404
+ # ORIENTATION CONFIGURATION - this eliminates the conditional branches
405
+ config = {
406
+ 'vertical': {
407
+ 'iteration_axis': 'y',
408
+ 'data_axis': 'x',
409
+ 'main_axis_label': 'Iteration (cycle threshold dotted red)',
410
+ 'residual_axis_label': 'Peak Residual',
411
+ 'flux_axis_label': 'Total Flux',
412
+ 'residual_axis_pos': 'above',
413
+ 'flux_axis_pos': 'above',
414
+ 'extra_ranges_key': 'extra_x_ranges',
415
+ 'glyph_coords': ('values', 'iterations'), # (x, y)
416
+ 'range_name_param': 'x_range_name'
417
+ },
418
+ 'horizontal': {
419
+ 'iteration_axis': 'x',
420
+ 'data_axis': 'y',
421
+ 'main_axis_label': 'Iteration (cycle threshold dotted red)',
422
+ 'residual_axis_label': 'Peak Residual',
423
+ 'flux_axis_label': 'Total Flux',
424
+ 'residual_axis_pos': 'right',
425
+ 'flux_axis_pos': 'right',
426
+ 'extra_ranges_key': 'extra_y_ranges',
427
+ 'glyph_coords': ('iterations', 'values'), # (x, y)
428
+ 'range_name_param': 'y_range_name'
429
+ }
430
+ }
431
+
432
+ cfg = config[orient]
433
+
434
+ # Create figure with no default axes; to control the axes they must
435
+ # be explicitly set to None
436
+ imdetails['gui']['convergence'] = figure( sizing_mode=sizing_mode,
437
+ x_axis_location=None, y_axis_location=None,
438
+ tools=[ hover ], toolbar_location=None, **kw )
439
+
440
+ # Set up extra ranges
441
+ setattr(imdetails['gui']['convergence'], cfg['extra_ranges_key'], {
442
+ 'residual_range': residual_range,
443
+ 'flux_range': flux_range
444
+ })
445
+
446
+ # Store references for JavaScript updates
447
+ imdetails['converge-ranges'] = {
448
+ 'residual_range': residual_range,
449
+ 'flux_range': flux_range
450
+ }
396
451
 
452
+ # Create main iteration axis
453
+ main_axis = LinearAxis(axis_label=cfg['main_axis_label'])
454
+ main_axis_pos = 'below' if cfg['iteration_axis'] == 'x' else 'left'
455
+ imdetails['gui']['convergence'].add_layout(main_axis, main_axis_pos)
456
+
457
+ # Create glyphs using configuration
458
+ x_coord, y_coord = cfg['glyph_coords']
459
+ range_param = {cfg['range_name_param']: 'residual_range'}
460
+
461
+ imdetails['gui']['convergence'].step( x_coord, y_coord, source=imdetails['converge-data']['cyclethreshold'],
462
+ line_color='red', line_dash='dotted', line_width=2, **range_param )
463
+ imdetails['gui']['convergence'].line( x_coord, y_coord, source=imdetails['converge-data']['residual'],
464
+ line_color=self._converge_color['residual'], **range_param )
465
+ imdetails['gui']['convergence'].scatter( x_coord, y_coord, source=imdetails['converge-data']['residual'],
466
+ color=self._converge_color['residual'], size=10, **range_param )
467
+
468
+ # Flux glyphs
469
+ range_param = {cfg['range_name_param']: 'flux_range'}
470
+ imdetails['gui']['convergence'].line( x_coord, y_coord, source=imdetails['converge-data']['flux'],
471
+ line_color=self._converge_color['flux'], **range_param )
472
+ imdetails['gui']['convergence'].scatter( x_coord, y_coord, source=imdetails['converge-data']['flux'],
473
+ color=self._converge_color['flux'], size=10, **range_param )
474
+
475
+ # Create and style residual axis
476
+ residual_axis_param = {cfg['range_name_param'].replace('_name', '_name'): 'residual_range'}
477
+ residual_axis = LinearAxis(axis_label=cfg['residual_axis_label'], **residual_axis_param)
478
+ residual_axis.axis_line_color = self._converge_color['residual']
479
+ residual_axis.major_label_text_color = self._converge_color['residual']
480
+ residual_axis.axis_label_text_color = self._converge_color['residual']
481
+ residual_axis.major_tick_line_color = self._converge_color['residual']
482
+ residual_axis.minor_tick_line_color = self._converge_color['residual']
483
+ imdetails['gui']['convergence'].add_layout(residual_axis, cfg['residual_axis_pos'])
484
+
485
+ # Create and style flux axis
486
+ flux_axis_param = {cfg['range_name_param'].replace('_name', '_name'): 'flux_range'}
487
+ flux_axis = LinearAxis(axis_label=cfg['flux_axis_label'], **flux_axis_param)
488
+ flux_axis.axis_line_color = self._converge_color['flux']
489
+ flux_axis.major_label_text_color = self._converge_color['flux']
490
+ flux_axis.axis_label_text_color = self._converge_color['flux']
491
+ flux_axis.major_tick_line_color = self._converge_color['flux']
492
+ flux_axis.minor_tick_line_color = self._converge_color['flux']
493
+ imdetails['gui']['convergence'].add_layout(flux_axis, cfg['flux_axis_pos'])
494
+
495
+ # Store axis references for JavaScript access
496
+ imdetails['converge-axes'] = {
497
+ 'residual_axis': residual_axis,
498
+ 'flux_axis': flux_axis
499
+ }
397
500
 
398
501
  def _launch_gui( self ):
399
502
  '''create and show GUI
@@ -534,23 +637,6 @@ class InteractiveCleanUI:
534
637
 
535
638
  self._clean['converge']['pipe'].register( self._clean['converge']['id'], convergence_handler )
536
639
 
537
- ###
538
- ### Data source that will be used for updating the convergence plot
539
- ###
540
- stokes = 0
541
- convergence = imdetails['converge']['chan'][0][stokes]
542
- imdetails['converge-data'] = { }
543
- imdetails['converge-data']['flux'] = ColumnDataSource( data=dict( values=convergence['modelFlux'], iterations=convergence['iterations'],
544
- cyclethreshold=convergence['cycleThresh'],
545
- stopDesc=list( map( ImagingDict.get_summaryminor_stopdesc, convergence['stopCode'] ) ),
546
- type=['flux'] * len(convergence['iterations']) ) )
547
- imdetails['converge-data']['residual'] = ColumnDataSource( data=dict( values=convergence['peakRes'], iterations=convergence['iterations'],
548
- cyclethreshold=convergence['cycleThresh'],
549
- stopDesc=list( map( ImagingDict.get_summaryminor_stopdesc, convergence['stopCode'] ) ),
550
- type=['residual'] * len(convergence['iterations'])) )
551
- imdetails['converge-data']['cyclethreshold'] = ColumnDataSource( data=dict( values=convergence['cycleThresh'], iterations=convergence['iterations'] ) )
552
-
553
-
554
640
  ###
555
641
  ### help page for cube interactions
556
642
  ###
@@ -585,8 +671,12 @@ class InteractiveCleanUI:
585
671
  imdetails['gui']['image']['src'] = imdetails['gui']['cube'].js_obj( )
586
672
  imdetails['gui']['image']['fig'] = imdetails['gui']['cube'].image( grid=False, height_policy='max', width_policy='max',
587
673
  channelcb=CustomJS( args=dict( img_state={ 'src': imdetails['gui']['image']['src'],
588
- 'flux': imdetails['converge-data']['flux'],
589
- 'residual': imdetails['converge-data']['residual'],
674
+ 'flux': { 'source': imdetails['converge-data']['flux'],
675
+ 'axis': imdetails['converge-axes']['flux_axis'],
676
+ 'range': imdetails['converge-ranges']['flux_range'] },
677
+ 'residual': { 'source': imdetails['converge-data']['residual'],
678
+ 'axis': imdetails['converge-axes']['residual_axis'],
679
+ 'range': imdetails['converge-ranges']['residual_range'] },
590
680
  'cyclethreshold': imdetails['converge-data']['cyclethreshold'] },
591
681
  imid=imid,
592
682
  ctrl={ 'converge': self._clean['converge'] },
@@ -702,9 +792,13 @@ class InteractiveCleanUI:
702
792
  'src': v['gui']['cube'].js_obj( ),
703
793
  'spectrum': v['gui']['spectrum'],
704
794
  'src': v['gui']['image']['src'],
705
- 'flux': v['converge-data']['flux'],
795
+ 'flux': { 'source': v['converge-data']['flux'],
796
+ 'axis': v['converge-axes']['flux_axis'],
797
+ 'range': v['converge-ranges']['flux_range'] },
706
798
  'cyclethreshold': v['converge-data']['cyclethreshold'],
707
- 'residual': v['converge-data']['residual'],
799
+ 'residual': { 'source': v['converge-data']['residual'],
800
+ 'axis': v['converge-axes']['residual_axis'],
801
+ 'range': v['converge-ranges']['residual_range'] },
708
802
  'navi': { 'slider': v['gui']['slider'],
709
803
  'goto': v['gui']['goto'],
710
804
  ## it doesn't seem like pixel tracking must be disabled
@@ -1216,7 +1310,52 @@ class InteractiveCleanUI:
1216
1310
  ### -- The "Insert here ..." code seems to be called when when the stokes plane is changed --
1217
1311
  ### -- but there have been no tclean iterations yet... --
1218
1312
  ### --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
1219
- 'update-converge': '''function update_convergence_single( target, data ) {
1313
+ 'update-converge': '''function updateAxisRange(source, range_obj, axis_obj, axis_name) {
1314
+ const data = source.data;
1315
+ const values = data['values'];
1316
+
1317
+ if (values.length === 0) return;
1318
+
1319
+ // Calculate data bounds
1320
+ const min_val = Math.min(...values);
1321
+ const max_val = Math.max(...values);
1322
+
1323
+ // Add padding (10% of range, with minimum padding)
1324
+ const span = max_val - min_val;
1325
+ const padding = max_val != min_val ? Math.max(span * 0.1, Math.abs(max_val * 0.02)) : Math.abs(max_val * 0.1);
1326
+
1327
+ // Update range
1328
+ range_obj.start = min_val - padding;
1329
+ range_obj.end = max_val + padding;
1330
+
1331
+ // Ensure ranges are valid (non-zero span)
1332
+ if ( range_obj.end - range_obj.start < 1e-10 ) {
1333
+ const center = range_obj.start
1334
+ if ( Math.abs(center) < 1e-10 ) {
1335
+ // If center is essentially 0
1336
+ range_obj.start = -0.1
1337
+ range_obj.end = 1.0
1338
+ } else {
1339
+ const span = Math.max(Math.abs(center * 0.2), 0.1)
1340
+ range_obj.start = center - span
1341
+ range_obj.end = center + span
1342
+ }
1343
+ }
1344
+
1345
+ // Optional: Update tick density
1346
+ if (axis_obj && axis_obj.ticker) {
1347
+ const new_span = range_obj.end - range_obj.start;
1348
+ if (new_span < 10) {
1349
+ axis_obj.ticker.desired_num_ticks = 8;
1350
+ } else if (new_span < 100) {
1351
+ axis_obj.ticker.desired_num_ticks = 10;
1352
+ } else {
1353
+ axis_obj.ticker.desired_num_ticks = 6;
1354
+ }
1355
+ }
1356
+ }
1357
+
1358
+ function update_convergence_single( target, data ) {
1220
1359
  const pos = target.src.cur_chan
1221
1360
  const imdata = data.get(pos[1]).get(pos[0])
1222
1361
  // chan----------------^^^^^^ ^^^^^^----stokes
@@ -1226,9 +1365,11 @@ class InteractiveCleanUI:
1226
1365
  const modelFlux = imdata.modelFlux
1227
1366
  const stopCode = imdata.stopCode
1228
1367
  const stopDesc = imdata.stopCode.map( code => stopdescmap.has(code) ? stopdescmap.get(code): "" )
1229
- target.residual.data = { iterations, cyclethreshold, stopDesc, values: peakRes, type: Array(iterations.length).fill('residual') }
1230
- target.flux.data = { iterations, cyclethreshold, stopDesc, values: modelFlux, type: Array(iterations.length).fill('flux') }
1368
+ target.residual.source.data = { iterations, cyclethreshold, stopDesc, values: peakRes, type: Array(iterations.length).fill('residual') }
1369
+ target.flux.source.data = { iterations, cyclethreshold, stopDesc, values: modelFlux, type: Array(iterations.length).fill('flux') }
1231
1370
  target.cyclethreshold.data = { iterations, values: cyclethreshold }
1371
+ updateAxisRange( target.flux.source, target.flux.range, target.flux.axis, 'Flux' )
1372
+ updateAxisRange( target.residual.source, target.residual.range, target.residual.axis, 'Residual' )
1232
1373
  }
1233
1374
 
1234
1375
  function update_convergence( recurse=false ) {