cubevis 0.5.23__py3-none-any.whl → 0.5.25__py3-none-any.whl
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.
- cubevis/__js__/cubevisjs.min.js +3 -2
- cubevis/__version__.py +1 -1
- cubevis/bokeh/models/__init__.py +1 -0
- cubevis/bokeh/models/_shared_dict.py +23 -0
- cubevis/toolbox/_cube.py +58 -16
- cubevis/toolbox/_interactive_clean_ui.mustache +239 -91
- cubevis/toolbox/_interactive_clean_ui.py +239 -91
- {cubevis-0.5.23.dist-info → cubevis-0.5.25.dist-info}/METADATA +1 -1
- {cubevis-0.5.23.dist-info → cubevis-0.5.25.dist-info}/RECORD +11 -10
- {cubevis-0.5.23.dist-info → cubevis-0.5.25.dist-info}/WHEEL +0 -0
- {cubevis-0.5.23.dist-info → cubevis-0.5.25.dist-info}/licenses/LICENSE +0 -0
|
@@ -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
|
|
@@ -278,6 +279,23 @@ class InteractiveCleanUI:
|
|
|
278
279
|
imdetails['path']['residual'] = join( output_dir, self._clean['gclean_paths'][imid]['residualname'] )
|
|
279
280
|
imdetails['path']['mask'] = join( output_dir, self._clean['gclean_paths'][imid]['maskname'] )
|
|
280
281
|
|
|
282
|
+
###
|
|
283
|
+
### There is one set of tclean controls for all images/outlier/etc. because
|
|
284
|
+
### in the final version gclean will handle the iterations for all fields...
|
|
285
|
+
###
|
|
286
|
+
cwidth = 64
|
|
287
|
+
cheight = 40
|
|
288
|
+
self._control['iteration'] = { }
|
|
289
|
+
self._control['iteration']['continue'] = TipButton( max_width=cwidth, max_height=cheight, name='continue',
|
|
290
|
+
icon=svg_icon(icon_name="iclean-continue", size=18),
|
|
291
|
+
tooltip=Tooltip( content=HTML( '''Stop after <b>one major cycle</b> or when any stopping criteria is met.''' ), position='left') )
|
|
292
|
+
self._control['iteration']['finish'] = TipButton( max_width=cwidth, max_height=cheight, name='finish',
|
|
293
|
+
icon=svg_icon(icon_name="iclean-finish", size=18),
|
|
294
|
+
tooltip=Tooltip( content=HTML( '''<b>Continue</b> until some stopping criteria is met.''' ), position='left') )
|
|
295
|
+
self._control['iteration']['stop'] = TipButton( button_type="danger", max_width=cwidth, max_height=cheight, name='stop',
|
|
296
|
+
icon=svg_icon(icon_name="iclean-stop", size=18),
|
|
297
|
+
tooltip=Tooltip( content=HTML( '''<p>Clicking a <font color="red">red</font> stop button will cause this tab to close and control will return to Python.<p>Clicking an <font color="orange">orange</font> stop button will cause <tt>tclean</tt> to stop after the current major cycle.''' ), position='left' ) )
|
|
298
|
+
|
|
281
299
|
for idx, (imid, imdetails) in enumerate(self._clean_targets.items( )):
|
|
282
300
|
imdetails['gui'] = { }
|
|
283
301
|
|
|
@@ -290,8 +308,13 @@ class InteractiveCleanUI:
|
|
|
290
308
|
###
|
|
291
309
|
imdetails['gui']['cube'] = CubeMask( imdetails['path']['residual'], mask=imdetails['path']['mask'], abort=self._abort_handler,
|
|
292
310
|
init_script=CustomJS( args=dict( initial_convergence_state=self._init_values["convergence_state"],
|
|
311
|
+
clean_ctrl=self._control['iteration'],
|
|
293
312
|
name=imid ),
|
|
294
|
-
code='''document._casa_convergence_data = initial_convergence_state
|
|
313
|
+
code='''document._casa_convergence_data = initial_convergence_state
|
|
314
|
+
clean_ctrl.continue.disable_add_sub = this.disable_add_sub.values
|
|
315
|
+
clean_ctrl.finish.disable_add_sub = this.disable_add_sub.values
|
|
316
|
+
clean_ctrl.stop.disable_add_sub = this.disable_add_sub.values
|
|
317
|
+
this.disable_add_sub.values.message = "cannot modify mask during cleaning"''' )
|
|
295
318
|
if idx == 0 else None )
|
|
296
319
|
|
|
297
320
|
###
|
|
@@ -343,57 +366,159 @@ class InteractiveCleanUI:
|
|
|
343
366
|
</div>'''
|
|
344
367
|
|
|
345
368
|
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
369
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
370
|
+
###
|
|
371
|
+
### Data source that will be used for updating the convergence plot
|
|
372
|
+
###
|
|
373
|
+
stokes = 0
|
|
374
|
+
convergence = imdetails['converge']['chan'][0][stokes]
|
|
375
|
+
imdetails['converge-data'] = { }
|
|
376
|
+
imdetails['converge-data']['flux'] = ColumnDataSource( data=dict( values=convergence['modelFlux'], iterations=convergence['iterations'],
|
|
377
|
+
cyclethreshold=convergence['cycleThresh'],
|
|
378
|
+
stopDesc=list( map( ImagingDict.get_summaryminor_stopdesc, convergence['stopCode'] ) ),
|
|
379
|
+
type=['flux'] * len(convergence['iterations']) ) )
|
|
380
|
+
imdetails['converge-data']['residual'] = ColumnDataSource( data=dict( values=convergence['peakRes'], iterations=convergence['iterations'],
|
|
381
|
+
cyclethreshold=convergence['cycleThresh'],
|
|
382
|
+
stopDesc=list( map( ImagingDict.get_summaryminor_stopdesc, convergence['stopCode'] ) ),
|
|
383
|
+
type=['residual'] * len(convergence['iterations'])) )
|
|
384
|
+
imdetails['converge-data']['cyclethreshold'] = ColumnDataSource( data=dict( values=convergence['cycleThresh'], iterations=convergence['iterations'] ) )
|
|
385
|
+
|
|
386
|
+
# Calculate explicit ranges for each dataset
|
|
387
|
+
flux_values = convergence['modelFlux']
|
|
388
|
+
residual_values = convergence['peakRes']
|
|
389
|
+
iterations = convergence['iterations']
|
|
390
|
+
cyclethresh_values = convergence['cycleThresh']
|
|
391
|
+
|
|
392
|
+
# Calculate ranges with padding
|
|
393
|
+
if len(flux_values) > 0:
|
|
394
|
+
flux_min, flux_max = np.min(flux_values), np.max(flux_values)
|
|
395
|
+
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)
|
|
396
|
+
else:
|
|
397
|
+
flux_min, flux_max, flux_padding = 0, 1, 0.1
|
|
372
398
|
|
|
399
|
+
if len(residual_values) > 0:
|
|
400
|
+
residual_min, residual_max = np.min(residual_values), np.max(residual_values)
|
|
401
|
+
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
402
|
else:
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
403
|
+
residual_min, residual_max, residual_padding = 0, 1, 0.1
|
|
404
|
+
|
|
405
|
+
# Create Range1d objects
|
|
406
|
+
flux_range = Range1d(start=flux_min - flux_padding, end=flux_max + flux_padding)
|
|
407
|
+
residual_range = Range1d(start=residual_min - residual_padding, end=residual_max + residual_padding)
|
|
408
|
+
|
|
409
|
+
# Ensure ranges are valid (non-zero span)
|
|
410
|
+
if flux_range.end - flux_range.start < 1e-10:
|
|
411
|
+
center = flux_range.start
|
|
412
|
+
if abs(center) < 1e-10: # If center is essentially 0
|
|
413
|
+
flux_range.start = -0.1
|
|
414
|
+
flux_range.end = 1.0
|
|
415
|
+
else:
|
|
416
|
+
span = max(abs(center * 0.2), 0.1)
|
|
417
|
+
flux_range.start = center - span
|
|
418
|
+
flux_range.end = center + span
|
|
419
|
+
|
|
420
|
+
if residual_range.end - residual_range.start < 1e-10:
|
|
421
|
+
center = residual_range.start
|
|
422
|
+
span = max(abs(center * 0.2), 0.1)
|
|
423
|
+
residual_range.start = center - span
|
|
424
|
+
residual_range.end = center + span
|
|
425
|
+
|
|
426
|
+
# ORIENTATION CONFIGURATION - this eliminates the conditional branches
|
|
427
|
+
config = {
|
|
428
|
+
'vertical': {
|
|
429
|
+
'iteration_axis': 'y',
|
|
430
|
+
'data_axis': 'x',
|
|
431
|
+
'main_axis_label': 'Iteration (cycle threshold dotted red)',
|
|
432
|
+
'residual_axis_label': 'Peak Residual',
|
|
433
|
+
'flux_axis_label': 'Total Flux',
|
|
434
|
+
'residual_axis_pos': 'above',
|
|
435
|
+
'flux_axis_pos': 'above',
|
|
436
|
+
'extra_ranges_key': 'extra_x_ranges',
|
|
437
|
+
'glyph_coords': ('values', 'iterations'), # (x, y)
|
|
438
|
+
'range_name_param': 'x_range_name'
|
|
439
|
+
},
|
|
440
|
+
'horizontal': {
|
|
441
|
+
'iteration_axis': 'x',
|
|
442
|
+
'data_axis': 'y',
|
|
443
|
+
'main_axis_label': 'Iteration (cycle threshold dotted red)',
|
|
444
|
+
'residual_axis_label': 'Peak Residual',
|
|
445
|
+
'flux_axis_label': 'Total Flux',
|
|
446
|
+
'residual_axis_pos': 'right',
|
|
447
|
+
'flux_axis_pos': 'right',
|
|
448
|
+
'extra_ranges_key': 'extra_y_ranges',
|
|
449
|
+
'glyph_coords': ('iterations', 'values'), # (x, y)
|
|
450
|
+
'range_name_param': 'y_range_name'
|
|
451
|
+
}
|
|
452
|
+
}
|
|
396
453
|
|
|
454
|
+
cfg = config[orient]
|
|
455
|
+
|
|
456
|
+
# Create figure with no default axes; to control the axes they must
|
|
457
|
+
# be explicitly set to None
|
|
458
|
+
imdetails['gui']['convergence'] = figure( sizing_mode=sizing_mode,
|
|
459
|
+
x_axis_location=None, y_axis_location=None,
|
|
460
|
+
tools=[ hover ], toolbar_location=None, **kw )
|
|
461
|
+
|
|
462
|
+
# Set up extra ranges
|
|
463
|
+
setattr(imdetails['gui']['convergence'], cfg['extra_ranges_key'], {
|
|
464
|
+
'residual_range': residual_range,
|
|
465
|
+
'flux_range': flux_range
|
|
466
|
+
})
|
|
467
|
+
|
|
468
|
+
# Store references for JavaScript updates
|
|
469
|
+
imdetails['converge-ranges'] = {
|
|
470
|
+
'residual_range': residual_range,
|
|
471
|
+
'flux_range': flux_range
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
# Create main iteration axis
|
|
475
|
+
main_axis = LinearAxis(axis_label=cfg['main_axis_label'])
|
|
476
|
+
main_axis_pos = 'below' if cfg['iteration_axis'] == 'x' else 'left'
|
|
477
|
+
imdetails['gui']['convergence'].add_layout(main_axis, main_axis_pos)
|
|
478
|
+
|
|
479
|
+
# Create glyphs using configuration
|
|
480
|
+
x_coord, y_coord = cfg['glyph_coords']
|
|
481
|
+
range_param = {cfg['range_name_param']: 'residual_range'}
|
|
482
|
+
|
|
483
|
+
imdetails['gui']['convergence'].step( x_coord, y_coord, source=imdetails['converge-data']['cyclethreshold'],
|
|
484
|
+
line_color='red', line_dash='dotted', line_width=2, **range_param )
|
|
485
|
+
imdetails['gui']['convergence'].line( x_coord, y_coord, source=imdetails['converge-data']['residual'],
|
|
486
|
+
line_color=self._converge_color['residual'], **range_param )
|
|
487
|
+
imdetails['gui']['convergence'].scatter( x_coord, y_coord, source=imdetails['converge-data']['residual'],
|
|
488
|
+
color=self._converge_color['residual'], size=10, **range_param )
|
|
489
|
+
|
|
490
|
+
# Flux glyphs
|
|
491
|
+
range_param = {cfg['range_name_param']: 'flux_range'}
|
|
492
|
+
imdetails['gui']['convergence'].line( x_coord, y_coord, source=imdetails['converge-data']['flux'],
|
|
493
|
+
line_color=self._converge_color['flux'], **range_param )
|
|
494
|
+
imdetails['gui']['convergence'].scatter( x_coord, y_coord, source=imdetails['converge-data']['flux'],
|
|
495
|
+
color=self._converge_color['flux'], size=10, **range_param )
|
|
496
|
+
|
|
497
|
+
# Create and style residual axis
|
|
498
|
+
residual_axis_param = {cfg['range_name_param'].replace('_name', '_name'): 'residual_range'}
|
|
499
|
+
residual_axis = LinearAxis(axis_label=cfg['residual_axis_label'], **residual_axis_param)
|
|
500
|
+
residual_axis.axis_line_color = self._converge_color['residual']
|
|
501
|
+
residual_axis.major_label_text_color = self._converge_color['residual']
|
|
502
|
+
residual_axis.axis_label_text_color = self._converge_color['residual']
|
|
503
|
+
residual_axis.major_tick_line_color = self._converge_color['residual']
|
|
504
|
+
residual_axis.minor_tick_line_color = self._converge_color['residual']
|
|
505
|
+
imdetails['gui']['convergence'].add_layout(residual_axis, cfg['residual_axis_pos'])
|
|
506
|
+
|
|
507
|
+
# Create and style flux axis
|
|
508
|
+
flux_axis_param = {cfg['range_name_param'].replace('_name', '_name'): 'flux_range'}
|
|
509
|
+
flux_axis = LinearAxis(axis_label=cfg['flux_axis_label'], **flux_axis_param)
|
|
510
|
+
flux_axis.axis_line_color = self._converge_color['flux']
|
|
511
|
+
flux_axis.major_label_text_color = self._converge_color['flux']
|
|
512
|
+
flux_axis.axis_label_text_color = self._converge_color['flux']
|
|
513
|
+
flux_axis.major_tick_line_color = self._converge_color['flux']
|
|
514
|
+
flux_axis.minor_tick_line_color = self._converge_color['flux']
|
|
515
|
+
imdetails['gui']['convergence'].add_layout(flux_axis, cfg['flux_axis_pos'])
|
|
516
|
+
|
|
517
|
+
# Store axis references for JavaScript access
|
|
518
|
+
imdetails['converge-axes'] = {
|
|
519
|
+
'residual_axis': residual_axis,
|
|
520
|
+
'flux_axis': flux_axis
|
|
521
|
+
}
|
|
397
522
|
|
|
398
523
|
def _launch_gui( self ):
|
|
399
524
|
'''create and show GUI
|
|
@@ -485,24 +610,6 @@ class InteractiveCleanUI:
|
|
|
485
610
|
#print("%s: %s" % ( btn, self._clean_ids[btn] ) )
|
|
486
611
|
self._pipe['control'].register( self._clean_ids[btn], clean_handler )
|
|
487
612
|
|
|
488
|
-
|
|
489
|
-
###
|
|
490
|
-
### There is one set of tclean controls for all images/outlier/etc. because
|
|
491
|
-
### in the final version gclean will handle the iterations for all fields...
|
|
492
|
-
###
|
|
493
|
-
cwidth = 64
|
|
494
|
-
cheight = 40
|
|
495
|
-
self._control['iteration'] = { }
|
|
496
|
-
self._control['iteration']['continue'] = TipButton( max_width=cwidth, max_height=cheight, name='continue',
|
|
497
|
-
icon=svg_icon(icon_name="iclean-continue", size=18),
|
|
498
|
-
tooltip=Tooltip( content=HTML( '''Stop after <b>one major cycle</b> or when any stopping criteria is met.''' ), position='left') )
|
|
499
|
-
self._control['iteration']['finish'] = TipButton( max_width=cwidth, max_height=cheight, name='finish',
|
|
500
|
-
icon=svg_icon(icon_name="iclean-finish", size=18),
|
|
501
|
-
tooltip=Tooltip( content=HTML( '''<b>Continue</b> until some stopping criteria is met.''' ), position='left') )
|
|
502
|
-
self._control['iteration']['stop'] = TipButton( button_type="danger", max_width=cwidth, max_height=cheight, name='stop',
|
|
503
|
-
icon=svg_icon(icon_name="iclean-stop", size=18),
|
|
504
|
-
tooltip=Tooltip( content=HTML( '''<p>Clicking a <font color="red">red</font> stop button will cause this tab to close and control will return to Python.<p>Clicking an <font color="orange">orange</font> stop button will cause <tt>tclean</tt> to stop after the current major cycle.''' ), position='left' ) )
|
|
505
|
-
|
|
506
613
|
###
|
|
507
614
|
### The single SHARED help button will be supplied by the first CubeMask...
|
|
508
615
|
###
|
|
@@ -534,23 +641,6 @@ class InteractiveCleanUI:
|
|
|
534
641
|
|
|
535
642
|
self._clean['converge']['pipe'].register( self._clean['converge']['id'], convergence_handler )
|
|
536
643
|
|
|
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
644
|
###
|
|
555
645
|
### help page for cube interactions
|
|
556
646
|
###
|
|
@@ -585,8 +675,12 @@ class InteractiveCleanUI:
|
|
|
585
675
|
imdetails['gui']['image']['src'] = imdetails['gui']['cube'].js_obj( )
|
|
586
676
|
imdetails['gui']['image']['fig'] = imdetails['gui']['cube'].image( grid=False, height_policy='max', width_policy='max',
|
|
587
677
|
channelcb=CustomJS( args=dict( img_state={ 'src': imdetails['gui']['image']['src'],
|
|
588
|
-
'flux': imdetails['converge-data']['flux'],
|
|
589
|
-
|
|
678
|
+
'flux': { 'source': imdetails['converge-data']['flux'],
|
|
679
|
+
'axis': imdetails['converge-axes']['flux_axis'],
|
|
680
|
+
'range': imdetails['converge-ranges']['flux_range'] },
|
|
681
|
+
'residual': { 'source': imdetails['converge-data']['residual'],
|
|
682
|
+
'axis': imdetails['converge-axes']['residual_axis'],
|
|
683
|
+
'range': imdetails['converge-ranges']['residual_range'] },
|
|
590
684
|
'cyclethreshold': imdetails['converge-data']['cyclethreshold'] },
|
|
591
685
|
imid=imid,
|
|
592
686
|
ctrl={ 'converge': self._clean['converge'] },
|
|
@@ -695,6 +789,7 @@ class InteractiveCleanUI:
|
|
|
695
789
|
margin=(-1, 0, -10, 0), button_type='light',
|
|
696
790
|
stylesheets=[ InlineStyleSheet( css='''.bk-btn { border: 0px solid #ccc; padding: 0 var(--padding-vertical) var(--padding-horizontal); margin-top: 3px; }''' ) ] )
|
|
697
791
|
|
|
792
|
+
|
|
698
793
|
self._control['iteration']['cb'] = CustomJS( args=dict( images_state={ k: { 'status': v['gui']['stopcode'],
|
|
699
794
|
'automask': v['gui']['params']['automask'],
|
|
700
795
|
'iteration': v['gui']['params']['iteration'],
|
|
@@ -702,9 +797,13 @@ class InteractiveCleanUI:
|
|
|
702
797
|
'src': v['gui']['cube'].js_obj( ),
|
|
703
798
|
'spectrum': v['gui']['spectrum'],
|
|
704
799
|
'src': v['gui']['image']['src'],
|
|
705
|
-
'flux': v['converge-data']['flux'],
|
|
800
|
+
'flux': { 'source': v['converge-data']['flux'],
|
|
801
|
+
'axis': v['converge-axes']['flux_axis'],
|
|
802
|
+
'range': v['converge-ranges']['flux_range'] },
|
|
706
803
|
'cyclethreshold': v['converge-data']['cyclethreshold'],
|
|
707
|
-
'residual': v['converge-data']['residual'],
|
|
804
|
+
'residual': { 'source': v['converge-data']['residual'],
|
|
805
|
+
'axis': v['converge-axes']['residual_axis'],
|
|
806
|
+
'range': v['converge-ranges']['residual_range'] },
|
|
708
807
|
'navi': { 'slider': v['gui']['slider'],
|
|
709
808
|
'goto': v['gui']['goto'],
|
|
710
809
|
## it doesn't seem like pixel tracking must be disabled
|
|
@@ -1216,7 +1315,52 @@ class InteractiveCleanUI:
|
|
|
1216
1315
|
### -- The "Insert here ..." code seems to be called when when the stokes plane is changed --
|
|
1217
1316
|
### -- but there have been no tclean iterations yet... --
|
|
1218
1317
|
### --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---
|
|
1219
|
-
'update-converge': '''function
|
|
1318
|
+
'update-converge': '''function updateAxisRange(source, range_obj, axis_obj, axis_name) {
|
|
1319
|
+
const data = source.data;
|
|
1320
|
+
const values = data['values'];
|
|
1321
|
+
|
|
1322
|
+
if (values.length === 0) return;
|
|
1323
|
+
|
|
1324
|
+
// Calculate data bounds
|
|
1325
|
+
const min_val = Math.min(...values);
|
|
1326
|
+
const max_val = Math.max(...values);
|
|
1327
|
+
|
|
1328
|
+
// Add padding (10% of range, with minimum padding)
|
|
1329
|
+
const span = max_val - min_val;
|
|
1330
|
+
const padding = max_val != min_val ? Math.max(span * 0.1, Math.abs(max_val * 0.02)) : Math.abs(max_val * 0.1);
|
|
1331
|
+
|
|
1332
|
+
// Update range
|
|
1333
|
+
range_obj.start = min_val - padding;
|
|
1334
|
+
range_obj.end = max_val + padding;
|
|
1335
|
+
|
|
1336
|
+
// Ensure ranges are valid (non-zero span)
|
|
1337
|
+
if ( range_obj.end - range_obj.start < 1e-10 ) {
|
|
1338
|
+
const center = range_obj.start
|
|
1339
|
+
if ( Math.abs(center) < 1e-10 ) {
|
|
1340
|
+
// If center is essentially 0
|
|
1341
|
+
range_obj.start = -0.1
|
|
1342
|
+
range_obj.end = 1.0
|
|
1343
|
+
} else {
|
|
1344
|
+
const span = Math.max(Math.abs(center * 0.2), 0.1)
|
|
1345
|
+
range_obj.start = center - span
|
|
1346
|
+
range_obj.end = center + span
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
// Optional: Update tick density
|
|
1351
|
+
if (axis_obj && axis_obj.ticker) {
|
|
1352
|
+
const new_span = range_obj.end - range_obj.start;
|
|
1353
|
+
if (new_span < 10) {
|
|
1354
|
+
axis_obj.ticker.desired_num_ticks = 8;
|
|
1355
|
+
} else if (new_span < 100) {
|
|
1356
|
+
axis_obj.ticker.desired_num_ticks = 10;
|
|
1357
|
+
} else {
|
|
1358
|
+
axis_obj.ticker.desired_num_ticks = 6;
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
function update_convergence_single( target, data ) {
|
|
1220
1364
|
const pos = target.src.cur_chan
|
|
1221
1365
|
const imdata = data.get(pos[1]).get(pos[0])
|
|
1222
1366
|
// chan----------------^^^^^^ ^^^^^^----stokes
|
|
@@ -1226,9 +1370,11 @@ class InteractiveCleanUI:
|
|
|
1226
1370
|
const modelFlux = imdata.modelFlux
|
|
1227
1371
|
const stopCode = imdata.stopCode
|
|
1228
1372
|
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') }
|
|
1373
|
+
target.residual.source.data = { iterations, cyclethreshold, stopDesc, values: peakRes, type: Array(iterations.length).fill('residual') }
|
|
1374
|
+
target.flux.source.data = { iterations, cyclethreshold, stopDesc, values: modelFlux, type: Array(iterations.length).fill('flux') }
|
|
1231
1375
|
target.cyclethreshold.data = { iterations, values: cyclethreshold }
|
|
1376
|
+
updateAxisRange( target.flux.source, target.flux.range, target.flux.axis, 'Flux' )
|
|
1377
|
+
updateAxisRange( target.residual.source, target.residual.range, target.residual.axis, 'Residual' )
|
|
1232
1378
|
}
|
|
1233
1379
|
|
|
1234
1380
|
function update_convergence( recurse=false ) {
|
|
@@ -1312,6 +1458,7 @@ class InteractiveCleanUI:
|
|
|
1312
1458
|
)
|
|
1313
1459
|
}
|
|
1314
1460
|
)
|
|
1461
|
+
clean_ctrl.continue.disable_add_sub.disabled = true
|
|
1315
1462
|
clean_ctrl.continue.disabled = true
|
|
1316
1463
|
clean_ctrl.finish.disabled = true
|
|
1317
1464
|
clean_ctrl.stop.disabled = with_stop
|
|
@@ -1336,6 +1483,7 @@ class InteractiveCleanUI:
|
|
|
1336
1483
|
|
|
1337
1484
|
clean_ctrl.stop.disabled = false
|
|
1338
1485
|
if ( ! only_stop ) {
|
|
1486
|
+
clean_ctrl.continue.disable_add_sub.disabled = false
|
|
1339
1487
|
clean_ctrl.continue.disabled = false
|
|
1340
1488
|
clean_ctrl.finish.disabled = false
|
|
1341
1489
|
}
|