Skip to content

Commit

Permalink
CO2Leakage: Add options-button for plotting polygons and fault lines,…
Browse files Browse the repository at this point in the history
… plus some minor visual changes (#1239)

* CO2Leakage: Add options-button for toggling on/off fault lines, containment polygon and hazardous polygon.

* CO2Leakage: Change default color scale and plot formation 'All' if available.

* CO2Leakage: Update changelog.

* CO2Leakage: Add button for plotting wells and for filtering which wells to plot.
  • Loading branch information
AudunSektnanNR authored Oct 17, 2023
1 parent 58be779 commit bdaa9e0
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 11 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed
- [#1231](https://github.com/equinor/webviz-subsurface/pull/1231) - Upgrades to the `EnsembleTableProvider` that improves performance: it no longer relies on `fmu-ensemble` for loading csv-files and parameters into dataframes. Additional: added new plugin argument `drop_failed_realizations` to `VolumetricAnalysis` to be able to load in volumetrics files, if they exist, even though the ensemble has crashed.
- [#1239](https://github.com/equinor/webviz-subsurface/pull/1239) - Added button for options and global filters to `CO2Leakage`, plus some minor visual changes.
- [#1226](https://github.com/equinor/webviz-subsurface/pull/1226) - Various visual and performance upgrades to `CO2Leakage`: New UNSMRY plot, added option for hazardous polygon, changed some containment plots, added volume as alternative data source, and more.

## [0.2.20] - 2023-06-26

Expand Down
18 changes: 18 additions & 0 deletions webviz_subsurface/plugins/_co2_leakage/_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,9 @@ def __init__(
initial_surface,
self._map_attribute_names,
[c["name"] for c in self._color_tables], # type: ignore
self._well_pick_provider.well_names()
if self._well_pick_provider
else [],
),
self.Ids.MAIN_SETTINGS,
)
Expand Down Expand Up @@ -357,6 +360,8 @@ def make_unit_list(
Input(self._settings_component(ViewSettings.Ids.CM_MAX), "value"),
Input(self._settings_component(ViewSettings.Ids.PLUME_THRESHOLD), "value"),
Input(self._settings_component(ViewSettings.Ids.PLUME_SMOOTHING), "value"),
Input(ViewSettings.Ids.OPTIONS_DIALOG_OPTIONS, "value"),
Input(ViewSettings.Ids.OPTIONS_DIALOG_WELL_FILTER, "value"),
State(self._settings_component(ViewSettings.Ids.ENSEMBLE), "value"),
)
def update_map_attribute(
Expand All @@ -372,6 +377,8 @@ def update_map_attribute(
cm_max_val: Optional[float],
plume_threshold: Optional[float],
plume_smoothing: Optional[float],
options_dialog_options: List[int],
selected_wells: List[str],
ensemble: str,
) -> Tuple[List[Dict[Any, Any]], List[Any], Dict[Any, Any]]:
attribute = MapAttribute(attribute)
Expand Down Expand Up @@ -434,6 +441,8 @@ def update_map_attribute(
file_hazardous_boundary=self._file_hazardous_boundary,
well_pick_provider=self._well_pick_provider,
plume_extent_data=plume_polygon,
options_dialog_options=options_dialog_options,
selected_wells=selected_wells,
)
annotations = create_map_annotations(
formation=formation,
Expand All @@ -442,3 +451,12 @@ def update_map_attribute(
)
viewports = create_map_viewports()
return (layers, annotations, viewports)

@callback(
Output(ViewSettings.Ids.OPTIONS_DIALOG, "open"),
Input(ViewSettings.Ids.OPTIONS_DIALOG_BUTTON, "n_clicks"),
)
def open_close_options_dialog(_n_clicks: Optional[int]) -> bool:
if _n_clicks is not None:
return _n_clicks > 0
raise PreventUpdate
45 changes: 36 additions & 9 deletions webviz_subsurface/plugins/_co2_leakage/_utilities/callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from webviz_subsurface.plugins._co2_leakage._utilities.generic import (
Co2MassScale,
Co2VolumeScale,
LayoutLabels,
MapAttribute,
)
from webviz_subsurface.plugins._co2_leakage._utilities.summary_graphs import (
Expand Down Expand Up @@ -222,6 +223,7 @@ def create_map_viewports() -> Dict:
"colormap-layer",
"fault-polygons-layer",
"license-boundary-layer",
"hazardous-boundary-layer",
"well-picks-layer",
"plume-polygon-layer",
],
Expand All @@ -230,6 +232,7 @@ def create_map_viewports() -> Dict:
}


# pylint: disable=too-many-arguments
def create_map_layers(
formation: str,
surface_data: Optional[SurfaceData],
Expand All @@ -238,6 +241,8 @@ def create_map_layers(
file_hazardous_boundary: Optional[str],
well_pick_provider: Optional[WellPickProvider],
plume_extent_data: Optional[geojson.FeatureCollection],
options_dialog_options: List[int],
selected_wells: List[str],
) -> List[Dict]:
layers = []
if surface_data is not None:
Expand All @@ -256,7 +261,10 @@ def create_map_layers(
}
)

if fault_polygon_url is not None:
if (
fault_polygon_url is not None
and LayoutLabels.SHOW_FAULTPOLYGONS in options_dialog_options
):
layers.append(
{
"@@type": "FaultPolygonsLayer",
Expand All @@ -265,7 +273,10 @@ def create_map_layers(
"data": fault_polygon_url,
}
)
if file_containment_boundary is not None:
if (
file_containment_boundary is not None
and LayoutLabels.SHOW_CONTAINMENT_POLYGON in options_dialog_options
):
layers.append(
{
"@@type": "GeoJsonLayer",
Expand All @@ -274,9 +285,13 @@ def create_map_layers(
"data": _parse_polygon_file(file_containment_boundary),
"stroked": False,
"getFillColor": [0, 172, 0, 120],
"visible": True,
}
)
if file_hazardous_boundary is not None:
if (
file_hazardous_boundary is not None
and LayoutLabels.SHOW_HAZARDOUS_POLYGON in options_dialog_options
):
layers.append(
{
"@@type": "GeoJsonLayer",
Expand All @@ -285,19 +300,31 @@ def create_map_layers(
"data": _parse_polygon_file(file_hazardous_boundary),
"stroked": False,
"getFillColor": [200, 0, 0, 120],
"visible": True,
}
)
if well_pick_provider is not None:
if (
well_pick_provider is not None
and LayoutLabels.SHOW_WELLS in options_dialog_options
):
well_data = dict(well_pick_provider.get_geojson(selected_wells, formation))
if "features" in well_data and len(well_data["features"]) == 0:
warnings.warn(f'Formation name "{formation}" not found in well picks file.')
layers.append(
{
"@@type": "GeoJsonLayer",
"name": "Well Picks",
"id": "well-picks-layer",
"data": dict(
well_pick_provider.get_geojson(
well_pick_provider.well_names(), formation
)
),
"data": well_data,
"visible": True,
"getText": "@@=properties.attribute",
"getTextSize": 12,
"getTextAnchor": "start",
"pointType": "circle+text",
"lineWidthMinPixels": 2,
"pointRadiusMinPixels": 2,
"pickable": True,
"parameters": {"depthTest": False},
}
)
if plume_extent_data is not None:
Expand Down
24 changes: 24 additions & 0 deletions webviz_subsurface/plugins/_co2_leakage/_utilities/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,27 @@ class GraphSource(StrEnum):
CONTAINMENT_MASS = "Containment Data (mass)"
CONTAINMENT_VOLUME_ACTUAL = "Containment Data (volume, actual)"
CONTAINMENT_VOLUME_ACTUAL_SIMPLE = "Containment Data (volume, actual_simple)"


class LayoutLabels(str, Enum):
"""Text labels used in layout components"""

SHOW_FAULTPOLYGONS = "Show fault polygons"
SHOW_CONTAINMENT_POLYGON = "Show containment polygon"
SHOW_HAZARDOUS_POLYGON = "Show hazardous polygon"
SHOW_WELLS = "Show wells"
WELL_FILTER = "Well filter"
COMMON_SELECTIONS = "Options and global filters"


# pylint: disable=too-few-public-methods
class LayoutStyle:
"""CSS styling"""

OPTIONS_BUTTON = {
"marginBottom": "10px",
"width": "100%",
"height": "30px",
"line-height": "30px",
"background-color": "lightgrey",
}
94 changes: 92 additions & 2 deletions webviz_subsurface/plugins/_co2_leakage/views/mainview/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,19 @@
from webviz_subsurface.plugins._co2_leakage._utilities.generic import (
Co2MassScale,
GraphSource,
LayoutLabels,
LayoutStyle,
MapAttribute,
)


class ViewSettings(SettingsGroupABC):
class Ids(StrEnum):
OPTIONS_DIALOG_BUTTON = "options-dialog-button"
OPTIONS_DIALOG = "options-dialog"
OPTIONS_DIALOG_OPTIONS = "options-dialog-options"
OPTIONS_DIALOG_WELL_FILTER = "options-dialog-well-filter"

FORMATION = "formation"
ENSEMBLE = "ensemble"
REALIZATION = "realization"
Expand Down Expand Up @@ -50,16 +57,20 @@ def __init__(
initial_surface: Optional[str],
map_attribute_names: Dict[MapAttribute, str],
color_scale_names: List[str],
well_names: List[str],
):
super().__init__("Settings")
self._ensemble_paths = ensemble_paths
self._ensemble_surface_providers = ensemble_surface_providers
self._map_attribute_names = map_attribute_names
self._color_scale_names = color_scale_names
self._initial_surface = initial_surface
self._well_names = well_names

def layout(self) -> List[Component]:
return [
DialogLayout(self._well_names),
OpenDialogButton(),
EnsembleSelectorLayout(
self.register_component_unique_id(self.Ids.ENSEMBLE),
self.register_component_unique_id(self.Ids.REALIZATION),
Expand Down Expand Up @@ -130,7 +141,11 @@ def set_formations(
elif current_value in surfaces:
picked_formation = dash.no_update
else:
picked_formation = formations[0]["value"]
picked_formation = (
"all"
if any((x["value"] == "all" for x in formations))
else formations[0]["value"]
)
return formations, picked_formation

@callback(
Expand Down Expand Up @@ -181,6 +196,76 @@ def set_y_min_max(
return len(min_auto) == 1, len(max_auto) == 1


class OpenDialogButton(html.Button):
def __init__(self) -> None:
super().__init__(
LayoutLabels.COMMON_SELECTIONS,
id=ViewSettings.Ids.OPTIONS_DIALOG_BUTTON,
style=LayoutStyle.OPTIONS_BUTTON,
n_clicks=0,
)


class DialogLayout(wcc.Dialog):
"""Layout for the options dialog"""

def __init__(
self,
well_names: List[str],
) -> None:
checklist_options = []
checklist_values = []
checklist_options.append(LayoutLabels.SHOW_FAULTPOLYGONS)
checklist_values.append(LayoutLabels.SHOW_FAULTPOLYGONS)
checklist_options.append(LayoutLabels.SHOW_CONTAINMENT_POLYGON)
checklist_values.append(LayoutLabels.SHOW_CONTAINMENT_POLYGON)
checklist_options.append(LayoutLabels.SHOW_HAZARDOUS_POLYGON)
checklist_values.append(LayoutLabels.SHOW_HAZARDOUS_POLYGON)
checklist_options.append(LayoutLabels.SHOW_WELLS)
checklist_values.append(LayoutLabels.SHOW_WELLS)

super().__init__(
title=LayoutLabels.COMMON_SELECTIONS,
id=ViewSettings.Ids.OPTIONS_DIALOG,
draggable=True,
open=False,
children=[
wcc.Checklist(
id=ViewSettings.Ids.OPTIONS_DIALOG_OPTIONS,
options=[{"label": opt, "value": opt} for opt in checklist_options],
value=checklist_values,
),
wcc.FlexBox(
children=[
html.Div(
style={
"flex": 3,
"minWidth": "20px",
"display": "block" if well_names else "none",
},
children=WellFilter(well_names),
),
],
style={"width": "20vw"},
),
],
)


class WellFilter(html.Div):
def __init__(self, well_names: List[str]) -> None:
super().__init__(
style={"display": "block" if well_names else "none"},
children=wcc.SelectWithLabel(
label=LayoutLabels.WELL_FILTER,
id=ViewSettings.Ids.OPTIONS_DIALOG_WELL_FILTER,
options=[{"label": i, "value": i} for i in well_names],
value=well_names,
size=min(20, len(well_names)),
),
)


class FilterSelectorLayout(wcc.Selectors):
def __init__(self, formation_id: str):
super().__init__(
Expand Down Expand Up @@ -213,6 +298,11 @@ def __init__(
cm_min_auto_id: str,
cm_max_auto_id: str,
):
default_colormap = (
"turbo (Seq)"
if "turbo (Seq)" in color_scale_names
else color_scale_names[0]
)
super().__init__(
label="Map Settings",
open_details=True,
Expand Down Expand Up @@ -244,7 +334,7 @@ def __init__(
dcc.Dropdown(
id=colormap_id,
options=color_scale_names,
value=color_scale_names[0],
value=default_colormap,
clearable=False,
),
"Minimum",
Expand Down

0 comments on commit bdaa9e0

Please sign in to comment.