-
Notifications
You must be signed in to change notification settings - Fork 74
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
400 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,4 +15,4 @@ toolbar: | |
tray: | ||
- g-subset-plugin | ||
- g-gaussian-smooth | ||
- export | ||
- export |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
from .dq_utils import * # noqa | ||
from .data_quality import * # noqa |
197 changes: 197 additions & 0 deletions
197
jdaviz/configs/default/plugins/data_quality/data_quality.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,197 @@ | ||
import os | ||
from traitlets import Any, Dict, Bool, List, Unicode, observe | ||
|
||
import numpy as np | ||
from glue_jupyter.common.toolbar_vuetify import read_icon | ||
from echo import delay_callback | ||
from matplotlib.colors import hex2color | ||
|
||
from jdaviz.core.registries import tray_registry | ||
from jdaviz.core.template_mixin import ( | ||
PluginTemplateMixin, ViewerSelect, LayerSelect | ||
) | ||
from jdaviz.core.tools import ICON_DIR | ||
from jdaviz.configs.default.plugins.data_quality.dq_utils import ( | ||
decode_flags, generate_listed_colormap, dq_flag_map_paths, load_flag_map | ||
) | ||
|
||
__all__ = ['DataQuality'] | ||
|
||
telescope_names = { | ||
"jwst": "JWST", | ||
"roman": "Roman" | ||
} | ||
|
||
|
||
@tray_registry('g-data-quality', label="Data Quality", viewer_requirements="image") | ||
class DataQuality(PluginTemplateMixin): | ||
template_file = __file__, "data_quality.vue" | ||
|
||
viewer_multiselect = Bool(False).tag(sync=True) | ||
viewer_items = List().tag(sync=True) | ||
viewer_selected = Any().tag(sync=True) # Any needed for multiselect | ||
viewer_limits = Dict().tag(sync=True) | ||
|
||
# `layer` is the science data layer | ||
science_layer_multiselect = Bool(False).tag(sync=True) | ||
science_layer_items = List().tag(sync=True) | ||
science_layer_selected = Any().tag(sync=True) # Any needed for multiselect | ||
|
||
# `dq_layer` is teh data quality layer corresponding to the | ||
# science data in `layer` | ||
dq_layer_multiselect = Bool(False).tag(sync=True) | ||
dq_layer_items = List().tag(sync=True) | ||
dq_layer_selected = Any().tag(sync=True) # Any needed for multiselect | ||
|
||
flag_map_definitions = Dict().tag(sync=True) | ||
flag_map_selected = Any().tag(sync=True) | ||
flag_map_items = List().tag(sync=True) | ||
viewer_selected = Any().tag(sync=True) # Any needed for multiselect | ||
decoded_flags = List().tag(sync=True) | ||
|
||
icons = Dict().tag(sync=True) | ||
icon_radialtocheck = Unicode(read_icon(os.path.join(ICON_DIR, 'radialtocheck.svg'), 'svg+xml')).tag(sync=True) # noqa | ||
icon_checktoradial = Unicode(read_icon(os.path.join(ICON_DIR, 'checktoradial.svg'), 'svg+xml')).tag(sync=True) # noqa | ||
|
||
def __init__(self, *args, **kwargs): | ||
super().__init__(*args, **kwargs) | ||
|
||
self.icons = {k: v for k, v in self.app.state.icons.items()} | ||
|
||
self.viewer = ViewerSelect( | ||
self, 'viewer_items', 'viewer_selected', 'viewer_multiselect' | ||
) | ||
self.science_layer = LayerSelect( | ||
self, 'science_layer_items', 'science_layer_selected', | ||
'viewer_selected', 'science_layer_multiselect', is_root=True | ||
) | ||
|
||
self.dq_layer = LayerSelect( | ||
self, 'dq_layer_items', 'dq_layer_selected', | ||
'viewer_selected', 'dq_layer_multiselect', is_root=False, | ||
is_child_of=self.science_layer.selected | ||
) | ||
|
||
self.load_default_flag_maps() | ||
self.init_decoding() | ||
|
||
@observe('science_layer_selected') | ||
def update_dq_layer(self, *args): | ||
if not hasattr(self, 'dq_layer'): | ||
return | ||
|
||
self.dq_layer.filter_is_child_of = self.science_layer_selected | ||
self.dq_layer._update_layer_items() | ||
|
||
def load_default_flag_maps(self): | ||
for name in dq_flag_map_paths: | ||
self.flag_map_definitions[name] = load_flag_map(name) | ||
self.flag_map_items = self.flag_map_items + [telescope_names[name]] | ||
|
||
@property | ||
def multiselect(self): | ||
logging.warning(f"DeprecationWarning: multiselect has been replaced by separate viewer_multiselect and layer_multiselect and will be removed in the future. This currently evaluates viewer_multiselect or layer_multiselect") # noqa | ||
return self.viewer_multiselect or self.layer_multiselect | ||
|
||
@multiselect.setter | ||
def multiselect(self, value): | ||
logging.warning(f"DeprecationWarning: multiselect has been replaced by separate viewer_multiselect and layer_multiselect and will be removed in the future. This currently sets viewer_multiselect and layer_multiselect") # noqa | ||
self.viewer_multiselect = value | ||
self.layer_multiselect = value | ||
|
||
def vue_set_value(self, data): | ||
attr_name = data.get('name') | ||
value = data.get('value') | ||
setattr(self, attr_name, value) | ||
|
||
@property | ||
def unique_flags(self): | ||
selected_dq = self.dq_layer.selected_obj | ||
if not len(selected_dq): | ||
return [] | ||
|
||
dq = selected_dq[0].get_image_data() | ||
return np.unique(dq[~np.isnan(dq)]) | ||
|
||
@property | ||
def flag_map_definitions_selected(self): | ||
return self.flag_map_definitions[self.flag_map_selected.lower()] | ||
|
||
@property | ||
def validate_flag_decode_possible(self): | ||
return ( | ||
self.flag_map_selected is not None and | ||
len(self.dq_layer.selected_obj) > 0 | ||
) | ||
|
||
@observe('dq_layer_selected') | ||
def init_decoding(self, event={}): | ||
if not self.validate_flag_decode_possible: | ||
return | ||
|
||
unique_flags = self.unique_flags | ||
cmap, rgba_colors = generate_listed_colormap(n_flags=len(unique_flags)) | ||
self.decoded_flags = decode_flags( | ||
flag_map=self.flag_map_definitions_selected, | ||
unique_flags=unique_flags, | ||
rgba_colors=rgba_colors | ||
) | ||
|
||
viewer = self.viewer.selected_obj | ||
[dq_layer] = [ | ||
layer for layer in viewer.layers if | ||
layer.layer.label == self.dq_layer_selected | ||
] | ||
dq_layer.composite._allow_bad_alpha = True | ||
|
||
flag_bits = np.float32([flag['flag'] for flag in self.decoded_flags]) | ||
|
||
with delay_callback(dq_layer.state, 'alpha', 'cmap', 'v_min', 'v_max', 'stretch'): | ||
dq_layer.state.cmap = cmap | ||
|
||
dq_layer.state.stretch = 'lookup' | ||
stretch_object = dq_layer.state.stretch_object | ||
stretch_object.flags = flag_bits | ||
|
||
dq_layer.state.v_min = min(flag_bits) | ||
dq_layer.state.v_max = max(flag_bits) | ||
dq_layer.state.alpha = 0.9 | ||
|
||
@observe('decoded_flags') | ||
def update_cmap(self, event={}): | ||
viewer = self.viewer.selected_obj | ||
[dq_layer] = [ | ||
layer for layer in viewer.layers if | ||
layer.layer.label == self.dq_layer_selected | ||
] | ||
flag_bits = np.float32([flag['flag'] for flag in self.decoded_flags]) | ||
rgb_colors = [hex2color(flag['color']) for flag in self.decoded_flags] | ||
|
||
# update the colors of the listed colormap without | ||
# reassigning the layer.state.cmap object | ||
cmap = dq_layer.state.cmap | ||
cmap.colors = rgb_colors | ||
cmap._init() | ||
|
||
with delay_callback(dq_layer.state, 'v_min', 'v_max', 'alpha'): | ||
# trigger updates to cmap in viewer: | ||
dq_layer.update() | ||
|
||
# set correct stretch and limits: | ||
dq_layer.state.stretch = 'lookup' | ||
dq_layer.state.v_min = min(flag_bits) | ||
dq_layer.state.v_max = max(flag_bits) | ||
dq_layer.state.alpha = 0.9 | ||
|
||
@observe('science_layer_selected') | ||
def mission_or_instrument_from_meta(self, event): | ||
if not hasattr(self, 'science_layer'): | ||
return | ||
|
||
layer = self.science_layer.selected_obj | ||
if len(layer): | ||
# this is defined for JWST and ROMAN, should be upper case: | ||
telescope = layer[0].layer.meta.get('telescope', None) | ||
|
||
if telescope is not None: | ||
self.flag_map_selected = telescope_names[telescope.lower()] |
124 changes: 124 additions & 0 deletions
124
jdaviz/configs/default/plugins/data_quality/data_quality.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
<template> | ||
<j-tray-plugin | ||
:description="docs_description || 'Viewer and data/layer options.'" | ||
:link="docs_link || 'https://jdaviz.readthedocs.io/en/'+vdocs+'/'+config+'/plugins.html#plot-options'" | ||
@plugin-ping="plugin_ping($event)" | ||
:popout_button="popout_button"> | ||
|
||
<!-- VIEWER OPTIONS --> | ||
<plugin-viewer-select | ||
:items="viewer_items" | ||
:selected.sync="viewer_selected" | ||
:multiselect.sync="viewer_multiselect" | ||
:show_multiselect_toggle="viewer_multiselect || viewer_items.length > 1" | ||
:icon_checktoradial="icon_checktoradial" | ||
:icon_radialtocheck="icon_radialtocheck" | ||
:label="viewer_multiselect ? 'Viewers' : 'Viewer'" | ||
:show_if_single_entry="viewer_multiselect" | ||
:hint="viewer_multiselect ? 'Select viewers to set options simultaneously' : 'Select the viewer to set options.'" | ||
/> | ||
|
||
<plugin-layer-select | ||
:items="science_layer_items" | ||
:selected.sync="science_layer_selected" | ||
:multiselect="science_layer_multiselect" | ||
:icons="icons" | ||
:show_if_single_entry="true" | ||
:label="'Science data'" | ||
:hint="'Select the science data'" | ||
/> | ||
|
||
<plugin-layer-select | ||
:items="dq_layer_items" | ||
:selected.sync="dq_layer_selected" | ||
:multiselect="dq_layer_multiselect" | ||
:label="'Data quality'" | ||
:show_if_single_entry="true" | ||
:hint="'Select the data quality'" | ||
:icons="icons" | ||
/> | ||
|
||
<v-row> | ||
<v-select | ||
attach | ||
:menu-props="{ left: true }" | ||
:items="flag_map_items" | ||
v-model="flag_map_selected" | ||
label="Flag definitions" | ||
></v-select> | ||
</v-row> | ||
|
||
<j-plugin-section-header>Quality Flags</j-plugin-section-header> | ||
<v-row style="max-width: calc(100% - 80px)"> | ||
<v-col> | ||
Color | ||
</v-col> | ||
<v-col> | ||
<strong>Flag</strong> | ||
</v-col> | ||
<v-col> | ||
(Decomposed) | ||
</v-col> | ||
</v-row> | ||
<v-row> | ||
<v-expansion-panels accordion> | ||
<v-expansion-panel v-for="(item, index) in decoded_flags" key=":item"> | ||
<v-expansion-panel-header v-slot="{ open }"> | ||
<v-row no-gutters align="center" style="..."> | ||
<v-col cols=1> | ||
</v-col> | ||
<v-col cols=2> | ||
<j-tooltip tipid='plugin-line-lists-color-picker'> | ||
<v-menu> | ||
<template v-slot:activator="{ on }"> | ||
<span class="color-menu" | ||
:style="`background:${item.color}; cursor: pointer`" | ||
@click.stop="on.click" | ||
> </span> | ||
</template> | ||
<div @click.stop="" style="text-align: end; background-color: white"> | ||
<v-color-picker v-model="decoded_flags[index].color" | ||
@update:color="throttledSetColor($event.hexa)"> | ||
></v-color-picker> | ||
</div> | ||
</v-menu> | ||
</j-tooltip> | ||
</v-col> | ||
<v-col> | ||
<div> <strong>{{item.flag}}</strong> ({{Object.keys(item.decomposed).join(', ')}})</div> | ||
</v-col> | ||
</v-row> | ||
</v-expansion-panel-header> | ||
<v-expansion-panel-content> | ||
<v-col v-for="(item, key, index) in item.decomposed"> | ||
<span>{{item.name}} ({{key}}): {{item.description}}</span> | ||
</v-col> | ||
</v-expansion-panel-content> | ||
<v-expansion-panel> | ||
</v-expansion-panels> | ||
</v-row> | ||
</j-tray-plugin> | ||
</template> | ||
|
||
|
||
<script> | ||
module.exports = { | ||
created() { | ||
this.throttledSetColor = _.throttle( | ||
(v) => { this.color = v }, | ||
100); | ||
} | ||
} | ||
</script> | ||
|
||
<style> | ||
.v-slider { | ||
margin: 0px !important; | ||
} | ||
.color-menu { | ||
font-size: 16px; | ||
padding-left: 16px; | ||
border: 2px solid rgba(0,0,0,0.54); | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.