webviz-subsurface 0.2.33__py3-none-any.whl → 0.2.35__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.
- webviz_subsurface/_models/parameter_model.py +1 -0
- webviz_subsurface/_providers/ensemble_grid_provider/_xtgeo_to_vtk_explicit_structured_grid.py +1 -1
- webviz_subsurface/_providers/ensemble_grid_provider/grid_viz_service.py +6 -4
- webviz_subsurface/_providers/ensemble_summary_provider/_provider_impl_arrow_lazy.py +2 -2
- webviz_subsurface/_providers/ensemble_summary_provider/_provider_impl_arrow_presampled.py +1 -1
- webviz_subsurface/_providers/ensemble_summary_provider/_resampling.py +30 -22
- webviz_subsurface/_providers/ensemble_surface_provider/_provider_impl_file.py +1 -17
- webviz_subsurface/_providers/ensemble_surface_provider/_surface_to_float32_array.py +1 -1
- webviz_subsurface/_utils/parameter_response.py +3 -1
- webviz_subsurface/plugins/_bhp_qc/views/_view_functions.py +1 -1
- webviz_subsurface/plugins/_co2_leakage/_utilities/plume_extent.py +1 -1
- webviz_subsurface/plugins/_co2_leakage/views/mainview/mainview.py +2 -2
- webviz_subsurface/plugins/_grid_viewer_fmu/views/view_3d/view_elements/_vtk_view_3d_element.py +2 -2
- webviz_subsurface/plugins/_history_match.py +1 -1
- webviz_subsurface/plugins/_map_viewer_fmu/_tmp_well_pick_provider.py +3 -1
- webviz_subsurface/plugins/_map_viewer_fmu/_types.py +1 -0
- webviz_subsurface/plugins/_map_viewer_fmu/_utils.py +18 -1
- webviz_subsurface/plugins/_map_viewer_fmu/callbacks.py +38 -12
- webviz_subsurface/plugins/_map_viewer_fmu/layout.py +32 -18
- webviz_subsurface/plugins/_map_viewer_fmu/map_viewer_fmu.py +33 -5
- webviz_subsurface/plugins/_rft_plotter/_plugin.py +5 -0
- webviz_subsurface/plugins/_rft_plotter/_utils/_rft_plotter_data_model.py +5 -2
- webviz_subsurface/plugins/_running_time_analysis_fmu.py +3 -3
- webviz_subsurface/plugins/_segy_viewer.py +6 -26
- webviz_subsurface/plugins/_surface_with_grid_cross_section.py +3 -16
- webviz_subsurface/plugins/_surface_with_seismic_cross_section.py +3 -16
- webviz_subsurface/plugins/_volumetric_analysis/controllers/distribution_controllers.py +33 -29
- webviz_subsurface/plugins/_volumetric_analysis/controllers/selections_controllers.py +8 -12
- webviz_subsurface/plugins/_volumetric_analysis/views/main_view.py +0 -3
- webviz_subsurface/plugins/_volumetric_analysis/views/selections_view.py +2 -12
- webviz_subsurface/plugins/_volumetric_analysis/volumetric_analysis.py +45 -3
- webviz_subsurface/plugins/_well_log_viewer/well_log_viewer.py +1 -1
- {webviz_subsurface-0.2.33.dist-info → webviz_subsurface-0.2.35.dist-info}/METADATA +3 -4
- {webviz_subsurface-0.2.33.dist-info → webviz_subsurface-0.2.35.dist-info}/RECORD +39 -66
- tests/integration_tests/plugin_tests/__init__.py +0 -0
- tests/integration_tests/plugin_tests/test_bhp_qc.py +0 -12
- tests/integration_tests/plugin_tests/test_history_match.py +0 -18
- tests/integration_tests/plugin_tests/test_line_plotter_fmu.py +0 -29
- tests/integration_tests/plugin_tests/test_parameter_analysis.py +0 -26
- tests/integration_tests/plugin_tests/test_parameter_correlation.py +0 -25
- tests/integration_tests/plugin_tests/test_parameter_distribution.py +0 -13
- tests/integration_tests/plugin_tests/test_parameter_parallel_coordinates.py +0 -13
- tests/integration_tests/plugin_tests/test_parameter_response_correlation.py +0 -15
- tests/integration_tests/plugin_tests/test_property_statistics.py +0 -22
- tests/integration_tests/plugin_tests/test_pvt_plot.py +0 -15
- tests/integration_tests/plugin_tests/test_relative_permeability.py +0 -14
- tests/integration_tests/plugin_tests/test_reservoir_simulation_timeseries.py +0 -30
- tests/integration_tests/plugin_tests/test_reservoir_simulation_timeseries_onebyone.py +0 -16
- tests/integration_tests/plugin_tests/test_reservoir_simulation_timeseries_regional.py +0 -23
- tests/integration_tests/plugin_tests/test_rft_plotter.py +0 -43
- tests/integration_tests/plugin_tests/test_segy_viewer.py +0 -22
- tests/integration_tests/plugin_tests/test_simulation_timeseries_onebyone.py +0 -23
- tests/integration_tests/plugin_tests/test_structural_uncertainty.py +0 -244
- tests/integration_tests/plugin_tests/test_surface_viewer_fmu.py +0 -21
- tests/integration_tests/plugin_tests/test_surface_with_grid_crossection.py +0 -45
- tests/integration_tests/plugin_tests/test_surface_with_seismic_crossection.py +0 -35
- tests/integration_tests/plugin_tests/test_tornado_plotter_fmu.py +0 -14
- tests/integration_tests/plugin_tests/test_vfp_analysis.py +0 -13
- tests/integration_tests/plugin_tests/test_volumetric_analysis.py +0 -38
- tests/integration_tests/plugin_tests/test_well_log_viewer.py +0 -16
- webviz_subsurface/_providers/ensemble_summary_provider/dev_resampling_perf_testing.py +0 -112
- {webviz_subsurface-0.2.33.dist-info → webviz_subsurface-0.2.35.dist-info}/LICENSE +0 -0
- {webviz_subsurface-0.2.33.dist-info → webviz_subsurface-0.2.35.dist-info}/LICENSE.chromedriver +0 -0
- {webviz_subsurface-0.2.33.dist-info → webviz_subsurface-0.2.35.dist-info}/WHEEL +0 -0
- {webviz_subsurface-0.2.33.dist-info → webviz_subsurface-0.2.35.dist-info}/entry_points.txt +0 -0
- {webviz_subsurface-0.2.33.dist-info → webviz_subsurface-0.2.35.dist-info}/top_level.txt +0 -0
|
@@ -151,6 +151,7 @@ class ParametersModel:
|
|
|
151
151
|
# if mix of gen_kw and sensitivity ensembles add
|
|
152
152
|
# dummy sensitivvity columns to gen_kw ensembles
|
|
153
153
|
gen_kw_mask = self._dataframe["SENSNAME"].isnull()
|
|
154
|
+
self._dataframe["SENSNAME"] = self._dataframe["SENSNAME"].astype(str)
|
|
154
155
|
self._dataframe.loc[gen_kw_mask, "SENSNAME"] = "🎲"
|
|
155
156
|
self._dataframe.loc[gen_kw_mask, "SENSCASE"] = "p10_p90"
|
|
156
157
|
|
webviz_subsurface/_providers/ensemble_grid_provider/_xtgeo_to_vtk_explicit_structured_grid.py
CHANGED
|
@@ -75,7 +75,7 @@ def _create_vtk_esgrid_from_verts_and_conn(
|
|
|
75
75
|
vtk_cell_array.SetData(8, conn_idarr)
|
|
76
76
|
|
|
77
77
|
vtk_esgrid = vtkExplicitStructuredGrid()
|
|
78
|
-
vtk_esgrid.SetDimensions(point_dims.tolist())
|
|
78
|
+
vtk_esgrid.SetDimensions(point_dims.tolist()) # type: ignore
|
|
79
79
|
vtk_esgrid.SetPoints(vtk_points)
|
|
80
80
|
vtk_esgrid.SetCells(vtk_cell_array)
|
|
81
81
|
|
|
@@ -365,15 +365,15 @@ class GridVizService:
|
|
|
365
365
|
|
|
366
366
|
plane = vtkPlane()
|
|
367
367
|
plane.SetOrigin([x_0, y_0, 0])
|
|
368
|
-
plane.SetNormal(right_vec.tolist())
|
|
368
|
+
plane.SetNormal(right_vec.tolist()) # type: ignore
|
|
369
369
|
|
|
370
370
|
plane_0 = vtkPlane()
|
|
371
371
|
plane_0.SetOrigin([x_0, y_0, 0])
|
|
372
|
-
plane_0.SetNormal(fwd_vec.tolist())
|
|
372
|
+
plane_0.SetNormal(fwd_vec.tolist()) # type: ignore
|
|
373
373
|
|
|
374
374
|
plane_1 = vtkPlane()
|
|
375
375
|
plane_1.SetOrigin([x_1, y_1, 0])
|
|
376
|
-
plane_1.SetNormal((-fwd_vec).tolist())
|
|
376
|
+
plane_1.SetNormal((-fwd_vec).tolist()) # type: ignore
|
|
377
377
|
|
|
378
378
|
cutter_alg.SetPlane(plane)
|
|
379
379
|
cutter_alg.Update()
|
|
@@ -478,7 +478,9 @@ class GridVizService:
|
|
|
478
478
|
i_ref = reference(0)
|
|
479
479
|
j_ref = reference(0)
|
|
480
480
|
k_ref = reference(0)
|
|
481
|
-
grid.ComputeCellStructuredCoords(
|
|
481
|
+
grid.ComputeCellStructuredCoords(
|
|
482
|
+
cell_id, i_ref, j_ref, k_ref, True # type: ignore[arg-type]
|
|
483
|
+
)
|
|
482
484
|
|
|
483
485
|
cell_property_val: Optional[np.ndarray] = None
|
|
484
486
|
if property_spec:
|
|
@@ -313,7 +313,7 @@ class ProviderImplArrowLazy(EnsembleSummaryProvider):
|
|
|
313
313
|
f"find_unique={et_find_unique_ms}ms)"
|
|
314
314
|
)
|
|
315
315
|
|
|
316
|
-
return intersected_dates.astype(datetime.datetime).tolist()
|
|
316
|
+
return intersected_dates.astype(datetime.datetime).tolist() # type: ignore
|
|
317
317
|
|
|
318
318
|
def get_vectors_df(
|
|
319
319
|
self,
|
|
@@ -377,7 +377,7 @@ class ProviderImplArrowLazy(EnsembleSummaryProvider):
|
|
|
377
377
|
table = table.filter(real_mask)
|
|
378
378
|
et_filter_ms = timer.lap_ms()
|
|
379
379
|
|
|
380
|
-
np_lookup_date = np.datetime64(date
|
|
380
|
+
np_lookup_date = np.datetime64(date).astype("M8[ms]")
|
|
381
381
|
table = sample_segmented_multi_real_table_at_date(table, np_lookup_date)
|
|
382
382
|
|
|
383
383
|
et_resample_ms = timer.lap_ms()
|
|
@@ -374,7 +374,7 @@ class ProviderImplArrowPresampled(EnsembleSummaryProvider):
|
|
|
374
374
|
f"find_unique={et_find_unique_ms}ms)"
|
|
375
375
|
)
|
|
376
376
|
|
|
377
|
-
return intersected_dates.astype(datetime.datetime).tolist()
|
|
377
|
+
return intersected_dates.astype(datetime.datetime).tolist() # type: ignore
|
|
378
378
|
|
|
379
379
|
def get_vectors_df(
|
|
380
380
|
self,
|
|
@@ -17,7 +17,7 @@ def _truncate_day_to_monday(datetime_day: np.datetime64) -> np.datetime64:
|
|
|
17
17
|
def _quarter_start_month(datetime_day: np.datetime64) -> np.datetime64:
|
|
18
18
|
# A bit hackish, utilizes the fact that datetime64 is relative to epoch
|
|
19
19
|
# 1970-01-01 which is the first day in Q1.
|
|
20
|
-
datetime_month =
|
|
20
|
+
datetime_month = datetime_day.astype("M8[M]")
|
|
21
21
|
return datetime_month - (datetime_month.astype(int) % 3)
|
|
22
22
|
|
|
23
23
|
|
|
@@ -30,44 +30,52 @@ def generate_normalized_sample_dates(
|
|
|
30
30
|
"""
|
|
31
31
|
|
|
32
32
|
if freq == Frequency.DAILY:
|
|
33
|
-
start =
|
|
34
|
-
stop =
|
|
33
|
+
start = min_date.astype("M8[D]")
|
|
34
|
+
stop = max_date.astype("M8[D]")
|
|
35
35
|
if stop < max_date:
|
|
36
|
-
stop += 1
|
|
37
|
-
sampledates = np.arange(start, stop + 1)
|
|
36
|
+
stop += np.timedelta64(1, "D")
|
|
37
|
+
sampledates = np.arange(start, stop + np.timedelta64(1, "D"))
|
|
38
|
+
|
|
38
39
|
elif freq == Frequency.WEEKLY:
|
|
39
|
-
start = _truncate_day_to_monday(
|
|
40
|
-
stop = _truncate_day_to_monday(
|
|
40
|
+
start = _truncate_day_to_monday(min_date.astype("M8[D]"))
|
|
41
|
+
stop = _truncate_day_to_monday(max_date.astype("M8[D]"))
|
|
41
42
|
if start > min_date:
|
|
42
|
-
start -= 7
|
|
43
|
+
start -= np.timedelta64(7, "D")
|
|
43
44
|
if stop < max_date:
|
|
44
|
-
stop += 7
|
|
45
|
-
sampledates = np.arange(
|
|
45
|
+
stop += np.timedelta64(7, "D")
|
|
46
|
+
sampledates = np.arange(
|
|
47
|
+
start, stop + np.timedelta64(1, "D"), np.timedelta64(7, "D")
|
|
48
|
+
)
|
|
49
|
+
|
|
46
50
|
elif freq == Frequency.MONTHLY:
|
|
47
|
-
start =
|
|
48
|
-
stop =
|
|
51
|
+
start = min_date.astype("M8[M]")
|
|
52
|
+
stop = max_date.astype("M8[M]")
|
|
49
53
|
if stop < max_date:
|
|
50
|
-
stop += 1
|
|
51
|
-
sampledates = np.arange(start, stop + 1)
|
|
54
|
+
stop += np.timedelta64(1, "M")
|
|
55
|
+
sampledates = np.arange(start, stop + np.timedelta64(1, "M"))
|
|
56
|
+
|
|
52
57
|
elif freq == Frequency.QUARTERLY:
|
|
53
58
|
start = _quarter_start_month(min_date)
|
|
54
59
|
stop = _quarter_start_month(max_date)
|
|
55
60
|
if stop < max_date:
|
|
56
|
-
stop += 3
|
|
57
|
-
sampledates = np.arange(
|
|
61
|
+
stop += np.timedelta64(3, "M")
|
|
62
|
+
sampledates = np.arange(
|
|
63
|
+
start, stop + np.timedelta64(1, "M"), np.timedelta64(3, "M")
|
|
64
|
+
)
|
|
65
|
+
|
|
58
66
|
elif freq == Frequency.YEARLY:
|
|
59
|
-
start =
|
|
60
|
-
stop =
|
|
67
|
+
start = min_date.astype("M8[Y]")
|
|
68
|
+
stop = max_date.astype("M8[Y]")
|
|
61
69
|
if stop < max_date:
|
|
62
|
-
stop += 1
|
|
63
|
-
sampledates = np.arange(start, stop + 1)
|
|
70
|
+
stop += np.timedelta64(1, "Y")
|
|
71
|
+
sampledates = np.arange(start, stop + np.timedelta64(1, "Y"))
|
|
72
|
+
|
|
64
73
|
else:
|
|
65
74
|
raise NotImplementedError(
|
|
66
75
|
f"Currently not supporting resampling to frequency {freq}."
|
|
67
76
|
)
|
|
68
77
|
|
|
69
|
-
sampledates = sampledates.astype("
|
|
70
|
-
|
|
78
|
+
sampledates = sampledates.astype("M8[ms]")
|
|
71
79
|
return sampledates
|
|
72
80
|
|
|
73
81
|
|
|
@@ -11,7 +11,6 @@ import xtgeo
|
|
|
11
11
|
from webviz_subsurface._utils.enum_shim import StrEnum
|
|
12
12
|
from webviz_subsurface._utils.perf_timer import PerfTimer
|
|
13
13
|
|
|
14
|
-
from ._stat_surf_cache import StatSurfCache
|
|
15
14
|
from ._surface_discovery import SurfaceFileInfo
|
|
16
15
|
from .ensemble_surface_provider import (
|
|
17
16
|
EnsembleSurfaceProvider,
|
|
@@ -26,7 +25,6 @@ LOGGER = logging.getLogger(__name__)
|
|
|
26
25
|
|
|
27
26
|
REL_SIM_DIR = "sim"
|
|
28
27
|
REL_OBS_DIR = "obs"
|
|
29
|
-
REL_STAT_CACHE_DIR = "stat_cache"
|
|
30
28
|
|
|
31
29
|
|
|
32
30
|
# pylint: disable=too-few-public-methods
|
|
@@ -53,8 +51,6 @@ class ProviderImplFile(EnsembleSurfaceProvider):
|
|
|
53
51
|
self._provider_dir = provider_dir
|
|
54
52
|
self._inventory_df = surface_inventory_df
|
|
55
53
|
|
|
56
|
-
self._stat_surf_cache = StatSurfCache(self._provider_dir / REL_STAT_CACHE_DIR)
|
|
57
|
-
|
|
58
54
|
@staticmethod
|
|
59
55
|
# pylint: disable=too-many-locals
|
|
60
56
|
def write_backing_store(
|
|
@@ -237,22 +233,10 @@ class ProviderImplFile(EnsembleSurfaceProvider):
|
|
|
237
233
|
) -> Optional[xtgeo.RegularSurface]:
|
|
238
234
|
timer = PerfTimer()
|
|
239
235
|
|
|
240
|
-
surf = self._stat_surf_cache.fetch(address)
|
|
241
|
-
if surf:
|
|
242
|
-
LOGGER.debug(
|
|
243
|
-
f"Fetched statistical surface from cache in: {timer.elapsed_s():.2f}s"
|
|
244
|
-
)
|
|
245
|
-
return surf
|
|
246
|
-
|
|
247
236
|
surf = self._create_statistical_surface(address)
|
|
248
|
-
et_create_s = timer.lap_s()
|
|
249
|
-
|
|
250
|
-
self._stat_surf_cache.store(address, surf)
|
|
251
|
-
et_write_cache_s = timer.lap_s()
|
|
252
237
|
|
|
253
238
|
LOGGER.debug(
|
|
254
|
-
f"Created
|
|
255
|
-
f"create={et_create_s:.2f}s, store={et_write_cache_s:.2f}s), "
|
|
239
|
+
f"Created statistical surface in: {timer.elapsed_s():.2f}s ("
|
|
256
240
|
f"[stat={address.statistic}, "
|
|
257
241
|
f"attr={address.attribute}, name={address.name}, date={address.datestr}]"
|
|
258
242
|
)
|
|
@@ -37,7 +37,9 @@ def filter_and_sum_responses(
|
|
|
37
37
|
if aggregation == "sum":
|
|
38
38
|
return df.groupby("REAL").sum().reset_index()[["REAL", response]]
|
|
39
39
|
if aggregation == "mean":
|
|
40
|
-
return
|
|
40
|
+
return (
|
|
41
|
+
df.groupby("REAL").mean(numeric_only=True).reset_index()[["REAL", response]]
|
|
42
|
+
)
|
|
41
43
|
raise ValueError(f"Unknown aggregation '{aggregation}'.")
|
|
42
44
|
|
|
43
45
|
|
|
@@ -20,7 +20,7 @@ def filter_df(df: pd.DataFrame, ensemble: str, wells: List[str]) -> pd.DataFrame
|
|
|
20
20
|
in statistics.
|
|
21
21
|
"""
|
|
22
22
|
columns = ["ENSEMBLE"] + [f"WBHP:{well}" for well in wells]
|
|
23
|
-
return df.loc[df["ENSEMBLE"] == ensemble][columns].replace(0, np.
|
|
23
|
+
return df.loc[df["ENSEMBLE"] == ensemble][columns].replace(0, np.nan)
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
def calc_statistics(df: pd.DataFrame) -> pd.DataFrame:
|
|
@@ -105,4 +105,4 @@ def _find_contours(
|
|
|
105
105
|
|
|
106
106
|
def _simplify(poly: np.ndarray, simplify_dist: float) -> List[List[float]]:
|
|
107
107
|
simplified = shapely.geometry.LineString(poly).simplify(simplify_dist)
|
|
108
|
-
return np.array(simplified.coords).tolist()
|
|
108
|
+
return np.array(simplified.coords).tolist() # type: ignore
|
|
@@ -6,7 +6,7 @@ from dash import html
|
|
|
6
6
|
from dash.development.base_component import Component
|
|
7
7
|
from webviz_config.utils import StrEnum
|
|
8
8
|
from webviz_config.webviz_plugin_subclasses import ViewABC, ViewElementABC
|
|
9
|
-
from webviz_subsurface_components import
|
|
9
|
+
from webviz_subsurface_components import SubsurfaceViewer
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class MainView(ViewABC):
|
|
@@ -48,7 +48,7 @@ class MapViewElement(ViewElementABC):
|
|
|
48
48
|
children=[
|
|
49
49
|
html.Div(
|
|
50
50
|
[
|
|
51
|
-
|
|
51
|
+
SubsurfaceViewer(
|
|
52
52
|
id=self.register_component_unique_id(
|
|
53
53
|
self.Ids.DECKGL_MAP
|
|
54
54
|
),
|
webviz_subsurface/plugins/_grid_viewer_fmu/views/view_3d/view_elements/_vtk_view_3d_element.py
CHANGED
|
@@ -2,7 +2,7 @@ from dash import dcc, html
|
|
|
2
2
|
from dash.development.base_component import Component
|
|
3
3
|
from webviz_config.utils import StrEnum
|
|
4
4
|
from webviz_config.webviz_plugin_subclasses import ViewElementABC
|
|
5
|
-
from webviz_subsurface_components import
|
|
5
|
+
from webviz_subsurface_components import SubsurfaceViewer
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class VTKView3D(ViewElementABC):
|
|
@@ -45,7 +45,7 @@ class VTKView3D(ViewElementABC):
|
|
|
45
45
|
html.Div(
|
|
46
46
|
style={"position": "absolute", "width": "100%", "height": "90%"},
|
|
47
47
|
children=[
|
|
48
|
-
|
|
48
|
+
SubsurfaceViewer(
|
|
49
49
|
id=self.register_component_unique_id(VTKView3D.Ids.VIEW),
|
|
50
50
|
layers=[
|
|
51
51
|
{
|
|
@@ -155,7 +155,7 @@ def _get_sorted_edges(number_observation_groups: int) -> Dict[str, list]:
|
|
|
155
155
|
np.random.chisquare(df=1, size=number_observation_groups)
|
|
156
156
|
)
|
|
157
157
|
|
|
158
|
-
sorted_values = np.flip(sorted_values, 0)
|
|
158
|
+
sorted_values = np.flip(sorted_values, 0) # type: ignore
|
|
159
159
|
|
|
160
160
|
p10 = np.percentile(sorted_values, 90, axis=1)
|
|
161
161
|
p90 = np.percentile(sorted_values, 10, axis=1)
|
|
@@ -3,6 +3,7 @@ from typing import Dict, List, Optional
|
|
|
3
3
|
import geojson
|
|
4
4
|
import pandas as pd
|
|
5
5
|
|
|
6
|
+
from webviz_subsurface._utils.colors import hex_to_rgb
|
|
6
7
|
from webviz_subsurface._utils.enum_shim import StrEnum
|
|
7
8
|
|
|
8
9
|
|
|
@@ -64,10 +65,11 @@ class WellPickProvider:
|
|
|
64
65
|
point = geojson.Point(coordinates=coords, validate=validate_geometry)
|
|
65
66
|
|
|
66
67
|
geocoll = geojson.GeometryCollection(geometries=[point])
|
|
67
|
-
|
|
68
68
|
properties = {
|
|
69
69
|
"name": row[WellPickTableColumns.WELL],
|
|
70
70
|
"attribute": str(row[attribute]),
|
|
71
|
+
"point_color": hex_to_rgb(row.get("point_color", "#000")),
|
|
72
|
+
"text_color": hex_to_rgb(row.get("text_color", "#000")),
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
feature = geojson.Feature(
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import base64
|
|
2
2
|
import io
|
|
3
3
|
import math
|
|
4
|
-
from typing import List
|
|
4
|
+
from typing import Dict, List
|
|
5
5
|
|
|
6
|
+
import geojson
|
|
7
|
+
import xtgeo
|
|
6
8
|
from PIL import Image, ImageDraw
|
|
7
9
|
|
|
8
10
|
|
|
@@ -39,3 +41,18 @@ def create_colormap_image_string(
|
|
|
39
41
|
draw.rectangle([(x_0, 0), (x_1, height)], fill=rgb_to_hex(color))
|
|
40
42
|
|
|
41
43
|
return f"data:image/png;base64,{image_to_base64(img)}"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def xtgeo_polygons_to_geojson(polygons: xtgeo.Polygons) -> Dict:
|
|
47
|
+
feature_arr = []
|
|
48
|
+
for name, polygon in polygons.dataframe.groupby("POLY_ID"):
|
|
49
|
+
coords = [list(zip(polygon.X_UTME, polygon.Y_UTMN))]
|
|
50
|
+
feature = geojson.Feature(
|
|
51
|
+
geometry=geojson.Polygon(coords),
|
|
52
|
+
properties={
|
|
53
|
+
"name": f"id:{name}",
|
|
54
|
+
"color": [200, 200, 200],
|
|
55
|
+
},
|
|
56
|
+
)
|
|
57
|
+
feature_arr.append(feature)
|
|
58
|
+
return geojson.FeatureCollection(features=feature_arr)
|
|
@@ -9,6 +9,7 @@ from typing import Any, Callable, Dict, List, Optional, Tuple, Union
|
|
|
9
9
|
|
|
10
10
|
import numpy as np
|
|
11
11
|
import webviz_subsurface_components as wsc
|
|
12
|
+
import xtgeo
|
|
12
13
|
from dash import ALL, MATCH, Input, Output, State, callback, callback_context, no_update
|
|
13
14
|
from dash.exceptions import PreventUpdate
|
|
14
15
|
from webviz_config import EncodedFile
|
|
@@ -32,7 +33,7 @@ from webviz_subsurface._providers import (
|
|
|
32
33
|
from ._layer_model import DeckGLMapLayersModel
|
|
33
34
|
from ._tmp_well_pick_provider import WellPickProvider
|
|
34
35
|
from ._types import LayerTypes, SurfaceMode
|
|
35
|
-
from ._utils import round_to_significant
|
|
36
|
+
from ._utils import round_to_significant, xtgeo_polygons_to_geojson
|
|
36
37
|
from .layout import (
|
|
37
38
|
DefaultSettings,
|
|
38
39
|
LayoutElements,
|
|
@@ -51,6 +52,8 @@ def plugin_callbacks(
|
|
|
51
52
|
surface_server: Union[SurfaceArrayServer, SurfaceImageServer],
|
|
52
53
|
ensemble_fault_polygons_providers: Dict[str, EnsembleFaultPolygonsProvider],
|
|
53
54
|
fault_polygons_server: FaultPolygonsServer,
|
|
55
|
+
field_outline_polygons: xtgeo.Polygons,
|
|
56
|
+
field_outline_color: Tuple[float, float, float],
|
|
54
57
|
map_surface_names_to_fault_polygons: Dict[str, str],
|
|
55
58
|
well_picks_provider: Optional[WellPickProvider],
|
|
56
59
|
fault_polygon_attribute: Optional[str],
|
|
@@ -518,9 +521,27 @@ def plugin_callbacks(
|
|
|
518
521
|
layer_data={
|
|
519
522
|
"data": well_picks_provider.get_geojson(
|
|
520
523
|
selected_wells, horizon_name
|
|
521
|
-
)
|
|
524
|
+
),
|
|
525
|
+
"getLineColor": "@@=properties.point_color",
|
|
526
|
+
"getFillColor": "@@=properties.point_color",
|
|
527
|
+
"getTextColor": "@@=properties.text_color",
|
|
528
|
+
},
|
|
529
|
+
)
|
|
530
|
+
if (
|
|
531
|
+
LayoutLabels.SHOW_FIELD_OUTLINE in options
|
|
532
|
+
and field_outline_polygons is not None
|
|
533
|
+
):
|
|
534
|
+
layer_model.update_layer_by_id(
|
|
535
|
+
layer_id=f"{LayoutElements.FIELD_OUTLINE_LAYER}-{idx}",
|
|
536
|
+
layer_data={
|
|
537
|
+
"data": xtgeo_polygons_to_geojson(field_outline_polygons),
|
|
538
|
+
"filled": False,
|
|
539
|
+
"depthTest": False,
|
|
540
|
+
"lineWidthMinPixels": 2,
|
|
541
|
+
"getLineColor": field_outline_color,
|
|
522
542
|
},
|
|
523
543
|
)
|
|
544
|
+
|
|
524
545
|
viewports = []
|
|
525
546
|
view_annotations = []
|
|
526
547
|
for idx, data in enumerate(surface_elements):
|
|
@@ -550,10 +571,13 @@ def plugin_callbacks(
|
|
|
550
571
|
"show3D": False,
|
|
551
572
|
"isSync": True,
|
|
552
573
|
"layerIds": [
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
574
|
+
(
|
|
575
|
+
f"{LayoutElements.MAP3D_LAYER}-{idx}"
|
|
576
|
+
if isinstance(surface_server, SurfaceArrayServer)
|
|
577
|
+
else f"{LayoutElements.COLORMAP_LAYER}-{idx}"
|
|
578
|
+
),
|
|
556
579
|
f"{LayoutElements.FAULTPOLYGONS_LAYER}-{idx}",
|
|
580
|
+
f"{LayoutElements.FIELD_OUTLINE_LAYER}-{idx}",
|
|
557
581
|
f"{LayoutElements.WELLS_LAYER}-{idx}",
|
|
558
582
|
],
|
|
559
583
|
"name": make_viewport_label(data, tab_name, multi),
|
|
@@ -851,13 +875,15 @@ def plugin_callbacks(
|
|
|
851
875
|
"colormap": {"value": colormap, "options": colormaps},
|
|
852
876
|
"color_range": {
|
|
853
877
|
"value": color_range,
|
|
854
|
-
"step":
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
878
|
+
"step": (
|
|
879
|
+
calculate_slider_step(
|
|
880
|
+
min_value=value_range[0],
|
|
881
|
+
max_value=value_range[1],
|
|
882
|
+
steps=100,
|
|
883
|
+
)
|
|
884
|
+
if value_range[0] != value_range[1]
|
|
885
|
+
else 0
|
|
886
|
+
),
|
|
861
887
|
"range": value_range,
|
|
862
888
|
},
|
|
863
889
|
}
|
|
@@ -3,7 +3,7 @@ from typing import Any, Callable, Dict, List, Union
|
|
|
3
3
|
|
|
4
4
|
import webviz_core_components as wcc
|
|
5
5
|
from dash import dcc, html
|
|
6
|
-
from webviz_subsurface_components import
|
|
6
|
+
from webviz_subsurface_components import SubsurfaceViewer # type: ignore
|
|
7
7
|
|
|
8
8
|
from webviz_subsurface._utils.enum_shim import StrEnum
|
|
9
9
|
|
|
@@ -37,15 +37,17 @@ class LayoutElements(StrEnum):
|
|
|
37
37
|
RANGE_RESET = "color-range-reset-button"
|
|
38
38
|
RESET_BUTTOM_CLICK = "color-range-reset-stored-state"
|
|
39
39
|
FAULTPOLYGONS = "fault-polygon-toggle"
|
|
40
|
+
FIELD_OUTLINE_TOGGLE = "field-outline-toggle"
|
|
40
41
|
WRAPPER = "wrapper-for-selector-component"
|
|
41
42
|
COLORWRAPPER = "wrapper-for-color-selector-component"
|
|
42
43
|
OPTIONS = "options"
|
|
43
44
|
|
|
44
45
|
COLORMAP_LAYER = "deckglcolormaplayer"
|
|
45
|
-
|
|
46
|
+
|
|
46
47
|
WELLS_LAYER = "deckglwelllayer"
|
|
47
48
|
MAP3D_LAYER = "deckglmap3dlayer"
|
|
48
49
|
FAULTPOLYGONS_LAYER = "deckglfaultpolygonslayer"
|
|
50
|
+
FIELD_OUTLINE_LAYER = "deckglfieldoutlinelayer"
|
|
49
51
|
REALIZATIONS_FILTER = "realization-filter-selector"
|
|
50
52
|
OPTIONS_DIALOG = "options-dialog"
|
|
51
53
|
|
|
@@ -69,8 +71,8 @@ class LayoutLabels(StrEnum):
|
|
|
69
71
|
LINK = "🔗 Link"
|
|
70
72
|
FAULTPOLYGONS = "Fault polygons"
|
|
71
73
|
SHOW_FAULTPOLYGONS = "Show fault polygons"
|
|
74
|
+
SHOW_FIELD_OUTLINE = "Show field outline"
|
|
72
75
|
SHOW_WELLS = "Show wells"
|
|
73
|
-
SHOW_HILLSHADING = "Hillshading"
|
|
74
76
|
COMMON_SELECTIONS = "Options and global filters"
|
|
75
77
|
REAL_FILTER = "Realization filter"
|
|
76
78
|
WELL_FILTER = "Well filter"
|
|
@@ -183,7 +185,7 @@ def main_layout(
|
|
|
183
185
|
realizations: List[int],
|
|
184
186
|
color_tables: List[Dict],
|
|
185
187
|
show_fault_polygons: bool = True,
|
|
186
|
-
|
|
188
|
+
show_field_outline: bool = False,
|
|
187
189
|
render_surfaces_as_images: bool = True,
|
|
188
190
|
) -> html.Div:
|
|
189
191
|
return html.Div(
|
|
@@ -240,9 +242,9 @@ def main_layout(
|
|
|
240
242
|
DialogLayout(
|
|
241
243
|
get_uuid,
|
|
242
244
|
show_fault_polygons,
|
|
245
|
+
show_field_outline,
|
|
243
246
|
well_names,
|
|
244
247
|
realizations,
|
|
245
|
-
hillshading_enabled,
|
|
246
248
|
),
|
|
247
249
|
]
|
|
248
250
|
)
|
|
@@ -269,7 +271,7 @@ class MapViewLayout(FullScreen):
|
|
|
269
271
|
) -> None:
|
|
270
272
|
super().__init__(
|
|
271
273
|
children=html.Div(
|
|
272
|
-
|
|
274
|
+
SubsurfaceViewer(
|
|
273
275
|
id={"id": get_uuid(LayoutElements.DECKGLMAP), "tab": tab},
|
|
274
276
|
layers=update_map_layers(1, render_surfaces_as_images),
|
|
275
277
|
colorTables=color_tables,
|
|
@@ -304,18 +306,20 @@ class DialogLayout(wcc.Dialog):
|
|
|
304
306
|
self,
|
|
305
307
|
get_uuid: Callable,
|
|
306
308
|
show_fault_polygons: bool,
|
|
309
|
+
show_field_outline: bool,
|
|
307
310
|
well_names: List[str],
|
|
308
311
|
realizations: List[int],
|
|
309
|
-
hillshading_enabled: bool = True,
|
|
310
312
|
) -> None:
|
|
311
|
-
checklist_options = [
|
|
312
|
-
checklist_values =
|
|
313
|
-
[LayoutLabels.SHOW_HILLSHADING] if hillshading_enabled else []
|
|
314
|
-
)
|
|
313
|
+
checklist_options = []
|
|
314
|
+
checklist_values = []
|
|
315
315
|
if show_fault_polygons:
|
|
316
316
|
checklist_options.append(LayoutLabels.SHOW_FAULTPOLYGONS)
|
|
317
317
|
checklist_values.append(LayoutLabels.SHOW_FAULTPOLYGONS)
|
|
318
318
|
|
|
319
|
+
if show_field_outline:
|
|
320
|
+
checklist_options.append(LayoutLabels.SHOW_FIELD_OUTLINE)
|
|
321
|
+
checklist_values.append(LayoutLabels.SHOW_FIELD_OUTLINE)
|
|
322
|
+
|
|
319
323
|
if well_names:
|
|
320
324
|
checklist_options.append(LayoutLabels.SHOW_WELLS)
|
|
321
325
|
checklist_values.append(LayoutLabels.SHOW_FAULTPOLYGONS)
|
|
@@ -358,9 +362,11 @@ class LinkCheckBox(wcc.Checklist):
|
|
|
358
362
|
clicked = selector in DefaultSettings.LINKED_SELECTORS.get(tab, [])
|
|
359
363
|
super().__init__(
|
|
360
364
|
id={
|
|
361
|
-
"id":
|
|
362
|
-
|
|
363
|
-
|
|
365
|
+
"id": (
|
|
366
|
+
get_uuid(LayoutElements.LINK)
|
|
367
|
+
if selector not in ["color_range", "colormap"]
|
|
368
|
+
else get_uuid(LayoutElements.COLORLINK)
|
|
369
|
+
),
|
|
364
370
|
"tab": tab,
|
|
365
371
|
"selector": selector,
|
|
366
372
|
},
|
|
@@ -570,9 +576,11 @@ class MapSelectorLayout(html.Div):
|
|
|
570
576
|
) -> None:
|
|
571
577
|
super().__init__(
|
|
572
578
|
style={
|
|
573
|
-
"display":
|
|
574
|
-
|
|
575
|
-
|
|
579
|
+
"display": (
|
|
580
|
+
"none"
|
|
581
|
+
if tab == Tabs.STATS and selector == MapSelector.MODE
|
|
582
|
+
else "block"
|
|
583
|
+
)
|
|
576
584
|
},
|
|
577
585
|
children=wcc.Selectors(
|
|
578
586
|
label=label,
|
|
@@ -805,7 +813,13 @@ def update_map_layers(
|
|
|
805
813
|
"parameters": {"depthTest": False},
|
|
806
814
|
}
|
|
807
815
|
)
|
|
808
|
-
|
|
816
|
+
layers.append(
|
|
817
|
+
{
|
|
818
|
+
"@@type": LayerTypes.FIELD_OUTLINE,
|
|
819
|
+
"id": f"{LayoutElements.FIELD_OUTLINE_LAYER}-{idx}",
|
|
820
|
+
"data": {"type": "FeatureCollection", "features": []},
|
|
821
|
+
}
|
|
822
|
+
)
|
|
809
823
|
if include_well_layer:
|
|
810
824
|
layers.append(
|
|
811
825
|
{
|