From 5359393490fea48bd4d60c4147619c1701801635 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20Bjerke?= Date: Tue, 17 Jan 2023 09:40:29 +0100 Subject: [PATCH] Allow creating view annotations using ViewAnnotation component. (#1384) * Allow creating view annotations using ViewAnnotation component. Allows creating view annotations using Dash. * Fixed ViewAnnotation handling with Dash * Removed SubsurfaceViewerDashWrapper story, as it only works to demonstrate Dash. * Applied black formatting. Co-authored-by: Havard Bjerke --- examples/deckgl_annotation.py | 121 ++++++++++++++++++ .../SubsurfaceViewerDashWrapper.stories.tsx | 59 --------- .../DeckGLMap/SubsurfaceViewerDashWrapper.tsx | 33 ++--- .../DeckGLMap/components/ViewAnnotation.tsx | 16 +++ react/src/lib/index.js | 2 + react/tsconfig.dash.json | 5 +- 6 files changed, 155 insertions(+), 81 deletions(-) create mode 100644 examples/deckgl_annotation.py delete mode 100644 react/src/lib/components/DeckGLMap/SubsurfaceViewerDashWrapper.stories.tsx create mode 100644 react/src/lib/components/DeckGLMap/components/ViewAnnotation.tsx diff --git a/examples/deckgl_annotation.py b/examples/deckgl_annotation.py new file mode 100644 index 000000000..5fce312e4 --- /dev/null +++ b/examples/deckgl_annotation.py @@ -0,0 +1,121 @@ +import numpy as np +import xtgeo +import dash +import webviz_subsurface_components as wsc + +from utils.xtgeo_surface_to_float32 import get_surface_float32 +from utils.xtgeo_polygons_to_json import xtgeo_polygons_to_polylines_geojson + +# Import a depth surface and a property surface using xtgeo +depth_surface = xtgeo.surface_from_file("examples/example-data/topvolantis_depth.gri") +property_surface = xtgeo.surface_from_file( + "examples/example-data/topvolantis_seismic_attribute.gri" +) +polygons = xtgeo.polygons_from_file( + "examples/example-data/topvolantis_faultpolygons.pol" +) + +app = dash.Dash(__name__) + +app.layout = wsc.SubsurfaceViewerDashWrapper( + id="deckgl-map", + layers=[ + { + "@@type": "AxesLayer", + "id": "axes-layer", + "bounds": [ + depth_surface.xmin, + depth_surface.ymin, + -np.nanmax(depth_surface.values), + depth_surface.xmax, + depth_surface.ymax, + np.nanmin(depth_surface.values), + ], + }, + { + "@@type": "MapLayer", + "id": "mesh-layer", + "meshUrl": "/map/mesh", + "frame": { + "origin": [depth_surface.xori, depth_surface.yori], + "count": [depth_surface.ncol, depth_surface.nrow], + "increment": [depth_surface.xinc, depth_surface.yinc], + "rotDeg": depth_surface.rotation, + }, + "contours": [0, 20], + "isContoursDepth": True, + "gridLines": False, + "material": True, + "colorMapName": "Physics", + "name": "mesh", + }, + { + "@@type": "MapLayer", + "id": "mesh-and-property-layer", + "meshUrl": "/map/mesh", + "propertiesUrl": "/map/property", + "frame": { + "origin": [depth_surface.xori, depth_surface.yori], + "count": [depth_surface.ncol, depth_surface.nrow], + "increment": [depth_surface.xinc, depth_surface.yinc], + "rotDeg": depth_surface.rotation, + }, + "isContoursDepth": True, + "gridLines": False, + "material": True, + "colorMapName": "Seismic", + "name": "mesh", + }, + { + "@@type": "FaultPolygonsLayer", + "id": "fault-layer", + "data": "/faults/faults.json", + "refine": False, + }, + ], + views={ + "layout": [1, 2], + "showLabel": True, + "viewports": [ + { + "id": "view_1", + "show3D": False, + "name": "Depth surface", + "layerIds": ["axes-layer", "mesh-layer"], + "isSync": True, + }, + { + "id": "view_2", + "show3D": False, + "name": "Property mapped on depth surface", + "layerIds": [ + "fault-layer", + "axes-layer", + "mesh-and-property-layer", + ], + "isSync": True, + }, + ], + }, + children=[ + wsc.ViewAnnotation(id="view_1", children=[wsc.ViewFooter(children="Hugin")]), + wsc.ViewAnnotation(id="view_2", children=[wsc.ViewFooter(children="sdfsfd")]), + ], +) + + +@app.server.route("/map/") +def send_map(map_name: str): + if map_name == "mesh": + return get_surface_float32(depth_surface) + if map_name == "property": + return get_surface_float32(property_surface) + + +@app.server.route("/faults/faults.json") +def send_faults(): + return xtgeo_polygons_to_polylines_geojson(polygons, xy_only=True) + + +if __name__ == "__main__": + app.run_server(debug=True) diff --git a/react/src/lib/components/DeckGLMap/SubsurfaceViewerDashWrapper.stories.tsx b/react/src/lib/components/DeckGLMap/SubsurfaceViewerDashWrapper.stories.tsx deleted file mode 100644 index f715aa21c..000000000 --- a/react/src/lib/components/DeckGLMap/SubsurfaceViewerDashWrapper.stories.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import React from "react"; -import { ComponentStory, ComponentMeta } from "@storybook/react"; -import { ViewFooter, SubsurfaceViewerDashWrapper } from "../.."; - -export default { - component: SubsurfaceViewerDashWrapper, - title: "DeckGLMap / SubsurfaceViewerDashWrapper", -} as ComponentMeta; - -const mapLayer = { - "@@type": "MapLayer", - id: "hugin", - meshUrl: "hugin_depth_25_m.float32", - frame: { - origin: [432150, 6475800], - count: [291, 229], - increment: [25, 25], - rotDeg: 0, - }, - propertiesUrl: "kh_netmap_25_m.float32", - contours: [0, 100], - material: false, -}; - -const DashWrapperTemplate: ComponentStory< - typeof SubsurfaceViewerDashWrapper -> = (args) => ; - -export const DashWrapperViewAnnotation = DashWrapperTemplate.bind({}); - -DashWrapperViewAnnotation.args = { - id: "dash_annotation", - layers: [ - mapLayer, - { - ...mapLayer, - id: "kh_netmap", - propertiesUrl: "hugin_depth_25_m.float32", - }, - ], - views: { - layout: [1, 2], - showLabel: true, - viewports: [ - { - id: "view_1", - layerIds: ["hugin"], - }, - { - id: "view_2", - layerIds: ["kh_netmap"], - }, - ], - }, - annotation: { - view_1: Hugin, - view_2: kH Netmap, - }, -}; diff --git a/react/src/lib/components/DeckGLMap/SubsurfaceViewerDashWrapper.tsx b/react/src/lib/components/DeckGLMap/SubsurfaceViewerDashWrapper.tsx index 4eea1555d..20530f1d0 100644 --- a/react/src/lib/components/DeckGLMap/SubsurfaceViewerDashWrapper.tsx +++ b/react/src/lib/components/DeckGLMap/SubsurfaceViewerDashWrapper.tsx @@ -65,21 +65,18 @@ export interface SubsurfaceViewerDashWrapperProps { cameraPosition?: ViewStateType | undefined; children?: React.ReactNode; - - /** - * A mapping associating annotation components to view ids. - * Example: {"view_1": } - */ - annotation?: Record; } -function mapAnnotation(annotation: Record) { - return Object.entries(annotation).map(([viewId, annotation]) => ( - // @ts-expect-error This is demonstrated to work with js, but with ts it gives error - - {annotation} - - )); +function mapAnnotation(annotationContainers: React.ReactNode) { + return React.Children.map(annotationContainers, (annotationContainer) => { + const viewId = (annotationContainer as React.ReactElement).key; + return ( + // @ts-expect-error This is demonstrated to work with js, but with ts it gives error + + {annotationContainer} + + ); + }); } const SubsurfaceViewerDashWrapper: React.FC< @@ -105,7 +102,6 @@ const SubsurfaceViewerDashWrapper: React.FC< triggerHome, triggerResetMultipleWells, children, - annotation = {}, }: SubsurfaceViewerDashWrapperProps) => { const mapArgs: DeckGLMapProps = { id: id, @@ -127,10 +123,9 @@ const SubsurfaceViewerDashWrapper: React.FC< getCameraPosition: getCameraPosition, triggerHome: triggerHome, triggerResetMultipleWells: triggerResetMultipleWells, - children: children, }; - return {mapAnnotation(annotation)}; + return {mapAnnotation(children)}; }; SubsurfaceViewerDashWrapper.defaultProps = { @@ -265,11 +260,7 @@ SubsurfaceViewerDashWrapper.propTypes = { */ onMouseEvent: PropTypes.func, - /** - * A mapping associating annotation components to view ids. - * Example: {"view_1": } - */ - annotation: PropTypes.any, + children: PropTypes.any, }; export default SubsurfaceViewerDashWrapper; diff --git a/react/src/lib/components/DeckGLMap/components/ViewAnnotation.tsx b/react/src/lib/components/DeckGLMap/components/ViewAnnotation.tsx new file mode 100644 index 000000000..e9599809d --- /dev/null +++ b/react/src/lib/components/DeckGLMap/components/ViewAnnotation.tsx @@ -0,0 +1,16 @@ +import React from "react"; +import PropTypes from "prop-types"; + +interface ViewAnnotationProps { + id: string; + children?: React.ReactNode; +} + +export const ViewAnnotation: React.FC = ({ children }) => { + return <> {children} ; +}; + +ViewAnnotation.propTypes = { + id: PropTypes.string.isRequired, + children: PropTypes.any, +}; diff --git a/react/src/lib/index.js b/react/src/lib/index.js index 7dbfd3fd9..f6a2a700b 100644 --- a/react/src/lib/index.js +++ b/react/src/lib/index.js @@ -30,6 +30,7 @@ import { WellsPickInfo } from "./components/DeckGLMap/layers/wells/wellsLayer"; import TerrainMapPickInfo from "./components/DeckGLMap/layers/terrain/terrainMapLayer"; import { FeatureCollection } from "@nebula.gl/edit-modes"; import { ViewFooter } from "./components/DeckGLMap/components/ViewFooter"; +import { ViewAnnotation } from "./components/DeckGLMap/components/ViewAnnotation"; export { HistoryMatch, @@ -58,4 +59,5 @@ export { ViewFooter, View, SubsurfaceViewerDashWrapper, + ViewAnnotation, }; diff --git a/react/tsconfig.dash.json b/react/tsconfig.dash.json index aae266b6c..2211064a6 100644 --- a/react/tsconfig.dash.json +++ b/react/tsconfig.dash.json @@ -4,5 +4,8 @@ "rootDir": "src/lib" }, "include": [], - "files": ["src/lib/components/DeckGLMap/components/ViewFooter.tsx"] + "files": [ + "src/lib/components/DeckGLMap/components/ViewFooter.tsx", + "src/lib/components/DeckGLMap/components/ViewAnnotation.tsx" + ] }