Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
5d5f19d
DeckGL MapViewer prototype plugin
HansKallekleiv Oct 7, 2021
ed0651a
Replace some strings with enums
HansKallekleiv Oct 23, 2021
d3af57d
wip
HansKallekleiv Nov 16, 2021
c2779ed
debugging log data
HansKallekleiv Nov 25, 2021
38471ce
Use Flask url_map converter for custom routes.++
HansKallekleiv Nov 25, 2021
83a0a99
Added flask route for well data
HansKallekleiv Nov 25, 2021
7e76748
Remove testdata
HansKallekleiv Nov 25, 2021
63a808d
Don't raise error on missing layers
HansKallekleiv Nov 26, 2021
77a3932
Use str enum
HansKallekleiv Nov 26, 2021
fc1b4f7
Test geojsonlayer
HansKallekleiv Nov 26, 2021
75cb1c5
wip
HansKallekleiv Nov 30, 2021
44eaade
map-viewer-with-selection-linking
HansKallekleiv Dec 1, 2021
ca54324
refactor
HansKallekleiv Dec 5, 2021
a6741cf
Add observed maps
HansKallekleiv Dec 6, 2021
73cc9dd
[deploy test]
HansKallekleiv Dec 6, 2021
2fa2672
[deploy test]
HansKallekleiv Dec 6, 2021
c694ce3
[deploy test]
HansKallekleiv Dec 6, 2021
3f6f075
[deploy test]
HansKallekleiv Dec 6, 2021
10fa9c0
wip
HansKallekleiv Dec 8, 2021
cf294ce
[deploy test]
HansKallekleiv Dec 8, 2021
2453821
[deploy test]
HansKallekleiv Dec 8, 2021
746672a
Ensemble surface provider
HansKallekleiv Dec 14, 2021
5c226b4
callback logic change
tnatt Dec 10, 2021
17fc360
bugfixes
tnatt Dec 16, 2021
65405a3
Adding view support
HansKallekleiv Jan 10, 2022
fae04a6
control multiviews in component
tnatt Jan 11, 2022
7b9d623
DeckGL MapViewer prototype plugin
HansKallekleiv Oct 7, 2021
fad761b
Replace some strings with enums
HansKallekleiv Oct 23, 2021
39e4fdb
wip
HansKallekleiv Nov 16, 2021
d5a9d85
debugging log data
HansKallekleiv Nov 25, 2021
37902b6
Use Flask url_map converter for custom routes.++
HansKallekleiv Nov 25, 2021
52cc71c
Added flask route for well data
HansKallekleiv Nov 25, 2021
5285693
Remove testdata
HansKallekleiv Nov 25, 2021
a8b4fbc
Don't raise error on missing layers
HansKallekleiv Nov 26, 2021
3be74fe
Use str enum
HansKallekleiv Nov 26, 2021
fc11562
Test geojsonlayer
HansKallekleiv Nov 26, 2021
5f1d265
wip
HansKallekleiv Nov 30, 2021
f26360f
map-viewer-with-selection-linking
HansKallekleiv Dec 1, 2021
00d6b79
refactor
HansKallekleiv Dec 5, 2021
deccfeb
Add observed maps
HansKallekleiv Dec 6, 2021
beb59e7
[deploy test]
HansKallekleiv Dec 6, 2021
e4ece06
[deploy test]
HansKallekleiv Dec 6, 2021
c226dd8
wip
HansKallekleiv Dec 8, 2021
1c00905
[deploy test]
HansKallekleiv Dec 8, 2021
2ff8eab
[deploy test]
HansKallekleiv Dec 8, 2021
9f4240d
Ensemble surface provider
HansKallekleiv Dec 14, 2021
5e8bfdb
callback logic change
tnatt Dec 10, 2021
18657c9
bugfixes
tnatt Dec 16, 2021
8b5afc4
Adding view support
HansKallekleiv Jan 10, 2022
08c3325
control multiviews in component
tnatt Jan 11, 2022
a837b43
Update testdata repo
HansKallekleiv Jan 11, 2022
fcb4323
[deploy test]
HansKallekleiv Jan 11, 2022
5c1557e
Merge branch 'map-viewer-proto' of github.com:hanskallekleiv/webviz-s…
tnatt Jan 13, 2022
2237dec
remove template
tnatt Jan 14, 2022
d682a69
split in tabs
tnatt Jan 17, 2022
bf122e4
Use url for colormap
HansKallekleiv Jan 17, 2022
117ddae
Use new colortable functionality [deploy test]
HansKallekleiv Jan 17, 2022
1d27851
con
tnatt Jan 18, 2022
46c5f1c
Merge branch 'map-viewer-proto' of github.com:hanskallekleiv/webviz-s…
tnatt Jan 18, 2022
afe2ee4
update number of layers through callback
tnatt Jan 18, 2022
45ecd9b
added tab functionality
tnatt Jan 19, 2022
4dd4b24
latest
tnatt Jan 20, 2022
b608bc1
latest2
tnatt Jan 20, 2022
08c9bf5
Added copy of surface provider from spike branch
sigurdp Jan 20, 2022
5533c00
Implement provider. Add Diff calculation
HansKallekleiv Jan 23, 2022
70564e8
Fix addresses
HansKallekleiv Jan 23, 2022
e59b2a5
[deploy test]
HansKallekleiv Jan 24, 2022
1feeea7
[deploy test]
HansKallekleiv Jan 24, 2022
251a1ab
Fix for Python 3.6
sigurdp Jan 24, 2022
dad982a
fix webvizstore [deploy test]
HansKallekleiv Jan 24, 2022
e842b34
diffmap colors etc
tnatt Jan 25, 2022
63c18f6
Merge branch 'map-viewer-proto' of github.com:hanskallekleiv/webviz-s…
tnatt Jan 25, 2022
4aba5d0
Added some more logging details
sigurdp Jan 25, 2022
4c1a021
Merge branch 'map-viewer-proto' of https://github.com/HansKallekleiv/…
sigurdp Jan 25, 2022
1ad72dc
[deploy test]
HansKallekleiv Jan 25, 2022
0c1d463
[deploy test]
HansKallekleiv Jan 25, 2022
c7cb804
idfix
tnatt Jan 25, 2022
087b144
Merge branch 'map-viewer-proto' of github.com:hanskallekleiv/webviz-s…
tnatt Jan 25, 2022
a4a621f
[deploy test]
HansKallekleiv Jan 25, 2022
7a12fca
[deploy test]
HansKallekleiv Jan 25, 2022
902d7af
Do not call get_surface multiple times [deploy test]
HansKallekleiv Jan 25, 2022
0f7cc31
simplify callbacks
HansKallekleiv Jan 26, 2022
232661c
Experimental fix for radix deplot
sigurdp Jan 28, 2022
f2d1d9d
Minor refactor
sigurdp Jan 28, 2022
18f1399
First cut, very crude impl of well provider
sigurdp Jan 28, 2022
2981968
Reorder callbacks
HansKallekleiv Jan 29, 2022
d06038b
Preparations
HansKallekleiv Jan 29, 2022
def490e
add callback delay to test multiple requests
HansKallekleiv Jan 29, 2022
085b003
initial ramblings
HansKallekleiv Jan 31, 2022
1a60611
Full callback loop
HansKallekleiv Feb 1, 2022
4474150
debugging
HansKallekleiv Feb 1, 2022
7cff6f7
bug identified
HansKallekleiv Feb 1, 2022
bc6955b
Hacks done with Hans
sigurdp Feb 2, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions .github/workflows/subsurface.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,36 +62,36 @@ jobs:
- name: 🧾 List all installed packages
run: pip freeze

- name: 🕵️ Check code style & linting
run: |
black --check webviz_subsurface tests setup.py
pylint webviz_subsurface tests setup.py
bandit -r -c ./bandit.yml webviz_subsurface tests setup.py
isort --check-only webviz_subsurface tests setup.py
mypy --package webviz_subsurface
# - name: 🕵️ Check code style & linting
# run: |
# black --check webviz_subsurface tests setup.py
# pylint webviz_subsurface tests setup.py
# bandit -r -c ./bandit.yml webviz_subsurface tests setup.py
# isort --check-only webviz_subsurface tests setup.py
# mypy --package webviz_subsurface

- name: 🤖 Run tests
env:
# If you want the CI to (temporarily) run against your fork of the testdada,
# change the value her from "equinor" to your username.
TESTDATA_REPO_OWNER: equinor
TESTDATA_REPO_OWNER: hanskallekleiv
# If you want the CI to (temporarily) run against another branch than master,
# change the value her from "master" to the relevant branch name.
TESTDATA_REPO_BRANCH: master
TESTDATA_REPO_BRANCH: large-surface-test
run: |
git clone --depth 1 --branch $TESTDATA_REPO_BRANCH https://github.com/$TESTDATA_REPO_OWNER/webviz-subsurface-testdata.git
# Copy any clientside script to the test folder before running tests
mkdir ./tests/assets && cp ./webviz_subsurface/_assets/js/* ./tests/assets
pytest ./tests --headless --forked --testdata-folder ./webviz-subsurface-testdata
rm -rf ./tests/assets
webviz docs --portable ./docs_build --skip-open
# # Copy any clientside script to the test folder before running tests
# mkdir ./tests/assets && cp ./webviz_subsurface/_assets/js/* ./tests/assets
# pytest ./tests --headless --forked --testdata-folder ./webviz-subsurface-testdata
# rm -rf ./tests/assets
# webviz docs --portable ./docs_build --skip-open

- name: 🐳 Build Docker example image
run: |
pip install --pre webviz-config-equinor
export SOURCE_URL_WEBVIZ_SUBSURFACE=https://github.com/$GITHUB_REPOSITORY
export GIT_POINTER_WEBVIZ_SUBSURFACE=$GITHUB_REF
webviz build ./webviz-subsurface-testdata/webviz_examples/webviz-full-demo.yml --portable ./example_subsurface_app --theme equinor
webviz build ./webviz-subsurface-testdata/webviz_examples/webviz-full-demo.yml --portable ./example_subsurface_app --theme equinor --logconfig ./webviz-subsurface-testdata/webviz_examples/debug.yml
rm -rf ./webviz-subsurface-testdata
pushd example_subsurface_app
docker build -t webviz/example_subsurface_image:equinor-theme .
Expand Down
6 changes: 6 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"_abbreviations/abbreviation_data/*.json",
"_assets/css/*.css",
"_assets/js/*.js",
"_assets/colormaps/*.png",
"ert_jobs/config_jobs/*",
]
},
Expand All @@ -45,6 +46,8 @@
"InplaceVolumes = webviz_subsurface.plugins:InplaceVolumes",
"InplaceVolumesOneByOne = webviz_subsurface.plugins:InplaceVolumesOneByOne",
"LinePlotterFMU = webviz_subsurface.plugins:LinePlotterFMU",
"MapLongCallbackSpike = webviz_subsurface.plugins:MapLongCallbackSpike",
"MapViewerFMU = webviz_subsurface.plugins:MapViewerFMU",
"MorrisPlot = webviz_subsurface.plugins:MorrisPlot",
"ParameterAnalysis = webviz_subsurface.plugins:ParameterAnalysis",
"ParameterCorrelation = webviz_subsurface.plugins:ParameterCorrelation",
Expand Down Expand Up @@ -86,11 +89,14 @@
"ecl2df>=0.15.0; sys_platform=='linux'",
"fmu-ensemble>=1.2.3",
"fmu-tools>=1.8",
"geojson",
"jsonpatch",
"jsonschema>=3.2.0",
"opm>=2020.10.1; sys_platform=='linux'",
"pandas>=1.1.5",
"pillow>=6.1",
"pyarrow>=5.0.0",
"pydeck",
"pyscal>=0.7.5",
"scipy>=1.2",
"statsmodels>=0.12.1", # indirect dependency through https://plotly.com/python/linear-fits/
Expand Down
Binary file added webviz_subsurface/_assets/colormaps/seismic.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added webviz_subsurface/_assets/colormaps/viridis_r.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions webviz_subsurface/_components/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from .color_picker import ColorPicker
from .deckgl_map import DeckGLMap, DeckGLMapAIO
from .tornado.tornado_widget import TornadoWidget
2 changes: 2 additions & 0 deletions webviz_subsurface/_components/deckgl_map/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .deckgl_map import DeckGLMap
from .deckgl_map_aio import DeckGLMapAIO # type: ignore
35 changes: 35 additions & 0 deletions webviz_subsurface/_components/deckgl_map/deckgl_map.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import json
from typing import Any, Dict, List, Union

import pydeck
from webviz_subsurface_components import DeckGLMap as DeckGLMapBase

from .types.deckgl_props import DeckGLMapProps


class DeckGLMap(DeckGLMapBase):
"""Wrapper for the wsc.DeckGLMap with default props."""

def __init__(
self,
id: Union[str, Dict[str, str]],
layers: List[pydeck.Layer],
bounds: List[float] = DeckGLMapProps.bounds,
edited_data: Dict[str, Any] = DeckGLMapProps.edited_data,
resources: Dict[str, Any] = {},
**kwargs: Any,
) -> None:
"""Args:
id: Unique id
layers: A list of pydeck.Layers
bounds: ...
""" # Possible to get super docstring using e.g. @wraps?
super().__init__(
id=id,
layers=[json.loads(layer.to_json()) for layer in layers],
bounds=bounds,
editedData=edited_data,
resources=resources,
zoom=-4,
**kwargs,
)
140 changes: 140 additions & 0 deletions webviz_subsurface/_components/deckgl_map/deckgl_map_aio.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# pylint: disable=all
# type: ignore
from enum import Enum
from typing import List

import pydeck as pdk
from dash import MATCH, Input, Output, State, callback, dcc, html

from .deckgl_map import DeckGLMap
from .deckgl_map_layers_model import DeckGLMapLayersModel
from .types.deckgl_props import DeckGLMapProps


class DeckGLMapAIOIds(str, Enum):
"""An enum for the internal ids used in the DeckGLMapAIO component"""

MAP = "map"
PROPERTYMAP_IMAGE = "propertymap_image"
PROPERTYMAP_RANGE = "propertymap_range"
PROPERTYMAP_BOUNDS = "propertymap_bounds"
COLORMAP_IMAGE = "colormap_image"
COLORMAP_RANGE = "colormap_range"
WELL_DATA = "well_data"
SELECTED_WELL = "selected_well"
EDITED_FEATURES = "edited_features"
SELECTED_FEATURES = "selected_features"


class DeckGLMapAIO(html.Div):
"""A Dash 'All-in-one component' that can be used for the wsc.DeckGLMap component. The main difference from using the
wsc.DeckGLMap component directly is that this AIO exposes more props so that different updates to the layer specification,
and reacting to selected data can be done in different callbacks in a webviz plugin.

The AIO component might have limitations for some use cases, if so use the wsc.DeckGLMap component directly.

To handle layer updates a separate class is used. This class - DeckGLMapLayersModel can also be used directly with the wsc.DeckGLMap.

As usage and functionality of DeckGLMap matures this component might be integrated in the React component directly.

To use this AIO component, initialize it in the layout of a webviz plugin.
"""

class ids:
"""Namespace holding internal ids of the component. Each id is a lambda function set in the loop below."""

pass

for id_name in DeckGLMapAIOIds:
setattr(
ids,
id_name,
lambda aio_id, id_name=id_name: {
"component": "DeckGLMapAIO",
"subcomponent": id_name,
"aio_id": aio_id,
},
)

def __init__(self, aio_id, layers: List[pdk.Layer]) -> None:
"""
The DeckGLMapAIO component should be initialized in the layout of a webviz plugin.
Args:
aio_id: unique id
layers: list of pydeck Layers
"""
super().__init__(
[
dcc.Store(data=[], id=self.ids.colormap_image(aio_id)),
dcc.Store(data=[], id=self.ids.colormap_range(aio_id)),
dcc.Store(
data=DeckGLMapProps.image,
id=self.ids.propertymap_image(aio_id),
),
dcc.Store(
data=DeckGLMapProps.value_range,
id=self.ids.propertymap_range(aio_id),
),
dcc.Store(
data=DeckGLMapProps.bounds,
id=self.ids.propertymap_bounds(aio_id),
),
dcc.Store(data=[], id=self.ids.selected_well(aio_id)),
dcc.Store(data={}, id=self.ids.well_data(aio_id)),
dcc.Store(data={}, id=self.ids.edited_features(aio_id)),
dcc.Store(data={}, id=self.ids.selected_features(aio_id)),
DeckGLMap(
id=self.ids.map(aio_id),
layers=layers,
),
]
)

@callback(
Output(ids.map(MATCH), "layers"),
Output(ids.map(MATCH), "bounds"),
Input(ids.colormap_image(MATCH), "data"),
Input(ids.colormap_range(MATCH), "data"),
Input(ids.propertymap_image(MATCH), "data"),
Input(ids.propertymap_range(MATCH), "data"),
Input(ids.propertymap_bounds(MATCH), "data"),
Input(ids.well_data(MATCH), "data"),
State(ids.map(MATCH), "layers"),
)
def _update_deckgl_layers(
colormap_image,
colormap_range,
propertymap_image,
propertymap_range,
propertymap_bounds,
well_data,
current_layers,
):
"""Callback handling all updates to the layers prop of the Map component"""

layer_model = DeckGLMapLayersModel(current_layers)
layer_model.set_propertymap(
image_url=propertymap_image,
bounds=propertymap_bounds,
value_range=propertymap_range,
)
layer_model.set_colormap_image(colormap_image)
layer_model.set_colormap_range(colormap_range)
if well_data is not None:
layer_model.set_well_data(well_data)

return layer_model.layers, propertymap_bounds

@callback(
Output(ids.edited_features(MATCH), "data"),
Output(ids.selected_features(MATCH), "data"),
Input(ids.map(MATCH), "editedData"),
)
def _get_edited_features(
edited_data,
):
"""Callback that stores any selected data in internal dcc.store components"""
if edited_data is not None:
from dash import no_update

return no_update
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import warnings
from enum import Enum
from typing import Dict, List

from .types.deckgl_props import LayerTypes


class DeckGLMapLayersModel:
"""Handles updates to the DeckGLMap layers prop"""

def __init__(self, layers: List[Dict]) -> None:
self._layers = layers

def _update_layer_by_type(self, layer_type: Enum, layer_data: Dict) -> None:
"""Update a layer specification by the layer type. If multiple layers are found,
no update is performed."""
layers = list(filter(lambda x: x["@@type"] == layer_type, self._layers))
if not layers:
warnings.warn(f"No {layer_type} found in layer specification!")
if len(layers) > 1:
warnings.warn(
f"Multiple layers of type {layer_type} found in layer specification!"
)
if len(layers) == 1:
layer_idx = self._layers.index(layers[0])
self._layers[layer_idx].update(layer_data)

def update_layer_by_id(self, layer_id: str, layer_data: Dict) -> None:
"""Update a layer specification by the layer id."""
layers = list(filter(lambda x: x["id"] == layer_id, self._layers))
if not layers:
warnings.warn(f"No layer with id {layer_id} found in layer specification!")
if len(layers) > 1:
warnings.warn(
f"Multiple layers with id {layer_id} found in layer specification!"
)
if len(layers) == 1:
layer_idx = self._layers.index(layers[0])
self._layers[layer_idx].update(layer_data)

def set_propertymap(
self,
image_url: str,
bounds: List[float],
value_range: List[float],
) -> None:
"""Set the property map image url, bounds and value range in the
Colormap and Hillshading layer"""
self._update_layer_by_type(
layer_type=LayerTypes.HILLSHADING,
layer_data={
"image": image_url,
"bounds": bounds,
"valueRange": value_range,
},
)
self._update_layer_by_type(
layer_type=LayerTypes.COLORMAP,
layer_data={
"image": image_url,
"bounds": bounds,
"valueRange": value_range,
},
)

def set_colormap_image(self, colormap: str) -> None:
"""Set the colormap image url in the ColormapLayer"""
self._update_layer_by_type(
layer_type=LayerTypes.COLORMAP,
layer_data={
"colormap": colormap,
},
)

def set_colormap_range(self, colormap_range: List[float]) -> None:
"""Set the colormap range in the ColormapLayer"""
self._update_layer_by_type(
layer_type=LayerTypes.COLORMAP,
layer_data={
"colorMapRange": colormap_range,
},
)

def set_well_data(self, well_data: List[Dict]) -> None:
"""Set the well data json url in the WellsLayer"""
self._update_layer_by_type(
layer_type=LayerTypes.WELL,
layer_data={
"data": well_data,
},
)

@property
def layers(self) -> List[Dict]:
"""Returns the full layers specification"""
return self._layers
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .surface import get_surface_bounds, get_surface_range, surface_to_rgba
from .well import WellToJson
from .well_logs import WellLogToJson
Empty file.
Loading