diff --git a/frontend/src/modules/Intersection/view/components/intersection.tsx b/frontend/src/modules/Intersection/view/components/intersection.tsx index 509e1e098..31cbabb39 100644 --- a/frontend/src/modules/Intersection/view/components/intersection.tsx +++ b/frontend/src/modules/Intersection/view/components/intersection.tsx @@ -2,8 +2,6 @@ import React from "react"; import { BoundingBox3d_api, WellboreCasing_api } from "@api"; import { Casing, IntersectionReferenceSystem, getSeismicInfo, getSeismicOptions } from "@equinor/esv-intersection"; -import { Button } from "@lib/components/Button"; -import { HoldPressedIntervalCallbackButton } from "@lib/components/HoldPressedIntervalCallbackButton/holdPressedIntervalCallbackButton"; import { useElementBoundingRect } from "@lib/hooks/useElementBoundingRect"; import { ColorScale, ColorScaleGradientType } from "@lib/utils/ColorScale"; import { IntersectionType, SeismicSliceImageData } from "@modules/Intersection/typesAndEnums"; @@ -14,18 +12,9 @@ import { LayerType, } from "@modules/_shared/components/EsvIntersection"; import { Viewport } from "@modules/_shared/components/EsvIntersection/esvIntersection"; -import { - AdditionalInformationKey, - HighlightItem, - HighlightItemShape, - ReadoutItem, -} from "@modules/_shared/components/EsvIntersection/types"; -import { getColorFromLayerData } from "@modules/_shared/components/EsvIntersection/utils/intersectionConversion"; -import { - getAdditionalInformationFromReadoutItem, - getLabelFromLayerData, -} from "@modules/_shared/components/EsvIntersection/utils/readoutItemUtils"; -import { Add, FilterCenterFocus, Remove } from "@mui/icons-material"; +import { HighlightItem, HighlightItemShape, ReadoutItem } from "@modules/_shared/components/EsvIntersection/types"; +import { ReadoutBox } from "@modules/_shared/components/EsvIntersection/utilityComponents/ReadoutBox"; +import { Toolbar } from "@modules/_shared/components/EsvIntersection/utilityComponents/Toolbar"; import { isEqual } from "lodash"; @@ -543,125 +532,3 @@ function GradientDef(props: GradientDefProps): React.ReactNode { ); } - -export type ReadoutBoxProps = { - readoutItems: ReadoutItem[]; -}; - -function additionalInformationItemToReadableString(key: string, value: unknown): string { - switch (key) { - case AdditionalInformationKey.CELL_INDEX: - return `Cell index: ${(value as number).toFixed(0)}`; - case AdditionalInformationKey.PROP_VALUE: - return `Property value: ${(value as number).toFixed(2)}`; - case AdditionalInformationKey.MD: - return `MD: ${(value as number).toFixed(2)}`; - case AdditionalInformationKey.MAX: - return `Max: ${(value as number).toFixed(2)}`; - case AdditionalInformationKey.MIN: - return `Min: ${(value as number).toFixed(2)}`; - case AdditionalInformationKey.P10: - return `P10: ${(value as number).toFixed(2)}`; - case AdditionalInformationKey.P90: - return `P90: ${(value as number).toFixed(2)}`; - case AdditionalInformationKey.P50: - return `P50: ${(value as number).toFixed(2)}`; - case AdditionalInformationKey.MEAN: - return `Mean: ${(value as number).toFixed(2)}`; - case AdditionalInformationKey.SCHEMATIC_INFO: - return (value as string[]).join(", "); - case AdditionalInformationKey.X: - return `X: ${(value as number).toFixed(2)}`; - case AdditionalInformationKey.Y: - return `Y: ${(value as number).toFixed(2)}`; - default: - return ""; - } -} - -function makeAdditionalInformation(item: ReadoutItem): React.ReactNode { - const additionalInformation = getAdditionalInformationFromReadoutItem(item); - return Object.entries(additionalInformation).map(([key, value], index) => { - return ( - - {additionalInformationItemToReadableString(key, value)} - - ); - }); -} - -function ReadoutBox(props: ReadoutBoxProps): React.ReactNode { - if (props.readoutItems.length === 0) { - return null; - } - - return ( -
- {props.readoutItems.map((item, index) => ( -
-
-
- {getLabelFromLayerData(item)} -
- {makeAdditionalInformation(item)} -
-
- ))} -
- ); -} - -type ToolbarProps = { - visible: boolean; - zFactor: number; - onFitInView: () => void; - onVerticalScaleIncrease: () => void; - onVerticalScaleDecrease: () => void; -}; - -function Toolbar(props: ToolbarProps): React.ReactNode { - function handleFitInViewClick() { - props.onFitInView(); - } - - function handleVerticalScaleIncrease() { - props.onVerticalScaleIncrease(); - } - - function handleVerticalScaleDecrease() { - props.onVerticalScaleDecrease(); - } - - if (!props.visible) { - return null; - } - - return ( -
- - - - - - {props.zFactor.toFixed(2)} - - - -
- ); -} - -function ToolBarDivider(): React.ReactNode { - return
; -} diff --git a/frontend/src/modules/StructuralUncertaintyIntersection/components/intersectionSettings.tsx b/frontend/src/modules/StructuralUncertaintyIntersection/components/intersectionSettings.tsx index d375563ef..1e52f6c81 100644 --- a/frontend/src/modules/StructuralUncertaintyIntersection/components/intersectionSettings.tsx +++ b/frontend/src/modules/StructuralUncertaintyIntersection/components/intersectionSettings.tsx @@ -17,20 +17,11 @@ export const IntersectionSettingsSelect: React.FC = ( props.onChange({ ...props.intersectionSettings, extension }); } }; - const handleZScaleChange = (e: any) => { - const zScale = parseInt(e.target.value, 10); - if (zScale >= 0) { - props.onChange({ ...props.intersectionSettings, zScale }); - } - }; return ( <> - ); }; diff --git a/frontend/src/modules/StructuralUncertaintyIntersection/loadModule.tsx b/frontend/src/modules/StructuralUncertaintyIntersection/loadModule.tsx index 548f3e887..d08f23798 100644 --- a/frontend/src/modules/StructuralUncertaintyIntersection/loadModule.tsx +++ b/frontend/src/modules/StructuralUncertaintyIntersection/loadModule.tsx @@ -2,15 +2,14 @@ import { ModuleRegistry } from "@framework/ModuleRegistry"; import { Settings } from "./settings"; import { State } from "./state"; +import { StatisticOption, VisualizationMode } from "./types"; import { View } from "./view"; -import { VisualizationMode } from "./types"; -import { StatisticFunction_api } from "@api"; const defaultState: State = { wellboreAddress: { uwi: "55/33-A-4", uuid: "drogon_horizontal", type: "smda" }, SurfaceSetAddress: null, visualizationMode: VisualizationMode.STATISTICAL_LINES, - statisticFunctions: [StatisticFunction_api.MEAN], + statisticFunctions: [StatisticOption.MEAN, StatisticOption.MIN_MAX, StatisticOption.P10_P90, StatisticOption.P50], stratigraphyColorMap: {}, intersectionSettings: { extension: 1000, diff --git a/frontend/src/modules/StructuralUncertaintyIntersection/settings.tsx b/frontend/src/modules/StructuralUncertaintyIntersection/settings.tsx index 44cb5f544..077191624 100644 --- a/frontend/src/modules/StructuralUncertaintyIntersection/settings.tsx +++ b/frontend/src/modules/StructuralUncertaintyIntersection/settings.tsx @@ -1,6 +1,6 @@ import React from "react"; -import { StatisticFunction_api, SurfaceAttributeType_api } from "@api"; +import { SurfaceAttributeType_api } from "@api"; import { EnsembleIdent } from "@framework/EnsembleIdent"; import { ModuleSettingsProps } from "@framework/Module"; import { useSettingsStatusWriter } from "@framework/StatusWriter"; @@ -29,6 +29,7 @@ import { RealizationsSelect } from "./components/realizationsSelect"; import { State } from "./state"; import { StatisticFunctionEnumToStringMapping, + StatisticOption, StratigraphyColorMap, SurfaceSetAddress, VisualizationMode, @@ -208,7 +209,7 @@ export function Settings({ syncHelper.publishValue(SyncSettingKey.WELLBORE, "global.syncValue.wellBore", newWellboreAddress); } function makeStatisticCheckboxes() { - return Object.values(StatisticFunction_api).map((value: StatisticFunction_api) => { + return Object.values(StatisticOption).map((value: StatisticOption) => { return ( , statistic: StatisticFunction_api) { + function handleStatisticsChange(event: React.ChangeEvent, statistic: StatisticOption) { setStatisticFunctions((prev) => { if (event.target.checked) { return prev ? [...prev, statistic] : [statistic]; diff --git a/frontend/src/modules/StructuralUncertaintyIntersection/state.ts b/frontend/src/modules/StructuralUncertaintyIntersection/state.ts index 09e059767..82c55c101 100644 --- a/frontend/src/modules/StructuralUncertaintyIntersection/state.ts +++ b/frontend/src/modules/StructuralUncertaintyIntersection/state.ts @@ -1,13 +1,12 @@ -import { StatisticFunction_api } from "@api"; import { Wellbore } from "@framework/types/wellbore"; -import { IntersectionSettings, SurfaceSetAddress, VisualizationMode } from "./types"; +import { IntersectionSettings, StatisticOption, SurfaceSetAddress, VisualizationMode } from "./types"; export interface State { wellboreAddress: Wellbore | null; SurfaceSetAddress: SurfaceSetAddress | null; visualizationMode: VisualizationMode; stratigraphyColorMap: { [name: string]: string }; - statisticFunctions: StatisticFunction_api[]; + statisticFunctions: StatisticOption[]; intersectionSettings: IntersectionSettings; } diff --git a/frontend/src/modules/StructuralUncertaintyIntersection/types.ts b/frontend/src/modules/StructuralUncertaintyIntersection/types.ts index c89108351..88f17798e 100644 --- a/frontend/src/modules/StructuralUncertaintyIntersection/types.ts +++ b/frontend/src/modules/StructuralUncertaintyIntersection/types.ts @@ -1,5 +1,3 @@ -import { StatisticFunction_api } from "@api"; - export type SeismicAddress = { caseUuid: string; ensemble: string; @@ -16,19 +14,23 @@ export type SurfaceSetAddress = { realizationNums: number[] | null; }; - export type IntersectionSettings = { extension: number; zScale: number; }; +export enum StatisticOption { + MEAN = "Mean", + MIN_MAX = "Min/Max", + P10_P90 = "P10/P90", + P50 = "P50", +} + export const StatisticFunctionEnumToStringMapping = { - [StatisticFunction_api.MEAN]: "Mean", - [StatisticFunction_api.MIN]: "Min", - [StatisticFunction_api.MAX]: "Max", - [StatisticFunction_api.P10]: "P10", - [StatisticFunction_api.P50]: "P50", - [StatisticFunction_api.P90]: "P90", + [StatisticOption.MEAN]: "Mean", + [StatisticOption.MIN_MAX]: "Min/Max", + [StatisticOption.P10_P90]: "P10/P90", + [StatisticOption.P50]: "P50", }; export enum VisualizationMode { diff --git a/frontend/src/modules/StructuralUncertaintyIntersection/view.tsx b/frontend/src/modules/StructuralUncertaintyIntersection/view.tsx index ffd2b8b0d..fb63705ff 100644 --- a/frontend/src/modules/StructuralUncertaintyIntersection/view.tsx +++ b/frontend/src/modules/StructuralUncertaintyIntersection/view.tsx @@ -1,27 +1,34 @@ import React from "react"; -import { IntersectionReferenceSystem, Trajectory } from "@equinor/esv-intersection"; +import { IntersectionReferenceSystem, SurfaceData, SurfaceLine, Trajectory } from "@equinor/esv-intersection"; import { ModuleViewProps } from "@framework/Module"; import { useViewStatusWriter } from "@framework/StatusWriter"; -import { useElementSize } from "@lib/hooks/useElementSize"; import { makeExtendedTrajectoryFromTrajectoryXyzPoints, makeTrajectoryXyzPointsFromWellboreTrajectory, } from "@modules/SeismicIntersection/utils/esvIntersectionDataConversion"; import { useWellboreTrajectoriesQuery } from "@modules/_shared/WellBore/queryHooks"; import { ContentError } from "@modules/_shared/components/ContentMessage"; +import { + EsvIntersection, + EsvIntersectionReadoutEvent, + LayerItem, + LayerType, + Viewport, +} from "@modules/_shared/components/EsvIntersection"; +import { SurfaceStatisticalFanchart } from "@modules/_shared/components/EsvIntersection/layers/SurfaceStatisticalFanchartCanvasLayer"; +import { ReadoutItem } from "@modules/_shared/components/EsvIntersection/types"; +import { ReadoutBox } from "@modules/_shared/components/EsvIntersection/utilityComponents/ReadoutBox"; +import { Toolbar } from "@modules/_shared/components/EsvIntersection/utilityComponents/Toolbar"; +import { makeSurfaceStatisticalFanchartFromRealizationSurface } from "@modules/_shared/components/EsvIntersection/utils/surfaceStatisticalFancharts"; import { isEqual } from "lodash"; -import Legend from "./components/Legend"; -import { EsvIntersection } from "./components/esvIntersection"; import { useSampleSurfaceInPointsQueries } from "./queryHooks"; import { State } from "./state"; +import { StatisticOption, VisualizationMode } from "./types"; export const View = ({ viewContext }: ModuleViewProps) => { - const wrapperDivRef = React.useRef(null); - const wrapperDivSize = useElementSize(wrapperDivRef); - const statusWriter = useViewStatusWriter(viewContext); const surfaceSetAddress = viewContext.useStoreValue("SurfaceSetAddress"); @@ -31,14 +38,17 @@ export const View = ({ viewContext }: ModuleViewProps) => { const intersectionSettings = viewContext.useStoreValue("intersectionSettings"); const stratigraphyColorMap = viewContext.useStoreValue("stratigraphyColorMap"); + const [readoutItems, setReadoutItems] = React.useState([]); + const [verticalScale, setVerticalScale] = React.useState(1); + const [viewport, setViewport] = React.useState([1000, 1650, 6000]); + const [referenceSystem, setReferenceSystem] = React.useState(null); + // Extended wellbore trajectory for creating intersection/fence extended on both sides of wellbore const [extendedWellboreTrajectory, setExtendedWellboreTrajectory] = React.useState(null); const [renderWellboreTrajectoryXyzPoints, setRenderWellboreTrajectoryXyzPoints] = React.useState( null ); - const width = wrapperDivSize.width; - const height = wrapperDivSize.height - 100; // Get well trajectories query const getWellTrajectoriesQuery = useWellboreTrajectoriesQuery(wellboreAddress ? [wellboreAddress.uuid] : undefined); @@ -63,13 +73,16 @@ export const View = ({ viewContext }: ModuleViewProps) => { // When new well trajectory 3D points are loaded, update the render trajectory and clear the seismic fence image if (!isEqual(trajectoryXyzPoints, renderWellboreTrajectoryXyzPoints)) { setRenderWellboreTrajectoryXyzPoints(trajectoryXyzPoints); + if (renderWellboreTrajectoryXyzPoints) { + setReferenceSystem(new IntersectionReferenceSystem(trajectoryXyzPoints)); + } } } - const x_points = extendedWellboreTrajectory?.points.map((coord) => coord[0]) ?? []; - const y_points = extendedWellboreTrajectory?.points.map((coord) => coord[1]) ?? []; + const xPoints = extendedWellboreTrajectory?.points.map((coord) => coord[0]) ?? []; + const yPoints = extendedWellboreTrajectory?.points.map((coord) => coord[1]) ?? []; - const cum_length = extendedWellboreTrajectory + const cumLength = extendedWellboreTrajectory ? IntersectionReferenceSystem.toDisplacement( extendedWellboreTrajectory.points, extendedWellboreTrajectory.offset @@ -82,8 +95,8 @@ export const View = ({ viewContext }: ModuleViewProps) => { surfaceSetAddress?.names ?? [], surfaceSetAddress?.attribute ?? "", surfaceSetAddress?.realizationNums ?? [], - x_points, - y_points, + xPoints, + yPoints, true ); @@ -98,36 +111,204 @@ export const View = ({ viewContext }: ModuleViewProps) => { if (errorString !== "") { statusWriter.addError(errorString); } - const stratigraphyColorLegendItems = - surfaceSetAddress?.names.map((key) => { - return { color: stratigraphyColorMap[key], label: key }; - }) ?? []; + + const handleReadoutItemsChange = React.useCallback(function handleReadoutItemsChange( + event: EsvIntersectionReadoutEvent + ): void { + setReadoutItems(event.readoutItems); + }, + []); + + const handleVerticalScaleIncrease = React.useCallback(function handleVerticalScaleIncrease(): void { + setVerticalScale((prev) => { + const newVerticalScale = prev + 0.1; + return newVerticalScale; + }); + }, []); + + const handleVerticalScaleDecrease = React.useCallback(function handleVerticalScaleIncrease(): void { + setVerticalScale((prev) => { + const newVerticalScale = Math.max(0.1, prev - 0.1); + return newVerticalScale; + }); + }, []); + + const handleFitInViewClick = React.useCallback(function handleFitInViewClick(): void { + setViewport([1000, 1650, 6000]); + }, []); + + const handleViewportChange = React.useCallback(function handleViewportChange(newViewport: Viewport): void { + setViewport(newViewport); + }, []); + + const layers: LayerItem[] = []; + if (cumLength && sampleSurfaceInPointsQueries.data) { + if ( + visualizationMode === VisualizationMode.STATISTICAL_LINES || + visualizationMode === VisualizationMode.STATISTICS_AND_REALIZATIONS + ) { + const surfaces = sampleSurfaceInPointsQueries.data; + const fancharts: SurfaceStatisticalFanchart[] = []; + const surfaceLines: SurfaceLine[] = []; + for (const surface of surfaces) { + const fanchart = makeSurfaceStatisticalFanchartFromRealizationSurface( + surface.realizationPoints.map((el) => el.sampled_values), + cumLength, + surface.surfaceName, + stratigraphyColorMap, + { + mean: statisticFunctions.includes(StatisticOption.MEAN), + minMax: statisticFunctions.includes(StatisticOption.MIN_MAX), + p10p90: statisticFunctions.includes(StatisticOption.P10_P90), + p50: statisticFunctions.includes(StatisticOption.P50), + } + ); + fancharts.push(fanchart); + + const surfaceLine: SurfaceLine = { + id: surface.surfaceName, + label: surface.surfaceName, + color: stratigraphyColorMap[surface.surfaceName] || "black", + data: fanchart.data.mean, + }; + + surfaceLines.push(surfaceLine); + } + + layers.push({ + type: LayerType.SURFACE_STATISTICAL_FANCHARTS_CANVAS, + id: "surface-fancharts", + options: { + data: { + fancharts, + }, + order: 3, + }, + hoverable: true, + }); + layers.push({ + type: LayerType.GEOMODEL_LABELS, + id: "surface-labels", + options: { + data: { + lines: surfaceLines, + areas: [], + }, + maxFontSize: 16, + minFontSize: 10, + order: 4, + }, + }); + } + if ( + visualizationMode === VisualizationMode.INDIVIDUAL_REALIZATIONS || + visualizationMode === VisualizationMode.STATISTICS_AND_REALIZATIONS + ) { + const surfaceValues: { surfaceName: string; name: string; realization: number; values: number[] }[] = []; + const surfaces = sampleSurfaceInPointsQueries.data; + for (const surface of surfaces) { + for (const realization of surface.realizationPoints) { + surfaceValues.push({ + surfaceName: surface.surfaceName, + name: `${surface.surfaceName}, real ${realization.realization}`, + realization: realization.realization, + values: realization.sampled_values, + }); + } + } + + const surfaceLines: SurfaceLine[] = []; + const labelSurfaceLines: SurfaceLine[] = []; + for (const realizationValues of surfaceValues) { + const surfaceLine: SurfaceLine = { + id: realizationValues.name + realizationValues.realization, + label: realizationValues.name, + color: stratigraphyColorMap[realizationValues.surfaceName] || "black", + data: realizationValues.values.map((z: number, idx) => { + return [cumLength[idx], z]; + }), + }; + surfaceLines.push(surfaceLine); + + if (realizationValues.realization === 0) { + labelSurfaceLines.push({ + ...surfaceLine, + label: realizationValues.surfaceName, + }); + } + } + + layers.push({ + type: LayerType.GEOMODEL_V2, + id: "surface-realizations", + options: { + data: { + lines: surfaceLines, + areas: [], + }, + order: 3, + }, + hoverable: true, + }); + + layers.push({ + type: LayerType.GEOMODEL_LABELS, + id: "surface-labels", + options: { + data: { + lines: labelSurfaceLines, + areas: [], + }, + maxFontSize: 16, + minFontSize: 10, + order: 4, + }, + }); + } + } + + if (renderWellboreTrajectoryXyzPoints) { + layers.push({ + type: LayerType.WELLBORE_PATH, + id: "wellbore-trajectory", + options: { + stroke: "red", + strokeWidth: "2", + order: 6, + }, + hoverable: true, + }); + } return ( -
+
{errorString !== "" ? ( {errorString} ) : ( <> + + -
- -
)}
diff --git a/frontend/src/modules/_shared/components/EsvIntersection/esvIntersection.tsx b/frontend/src/modules/_shared/components/EsvIntersection/esvIntersection.tsx index 6840d2d92..dc1cd53e3 100644 --- a/frontend/src/modules/_shared/components/EsvIntersection/esvIntersection.tsx +++ b/frontend/src/modules/_shared/components/EsvIntersection/esvIntersection.tsx @@ -197,20 +197,20 @@ function makeLayer( export function EsvIntersection(props: EsvIntersectionProps): React.ReactNode { const { onReadout, onViewportChange: onViewportChange } = props; - const [prevAxesOptions, setPrevAxesOptions] = React.useState(null); + const [prevAxesOptions, setPrevAxesOptions] = React.useState(undefined); const [prevIntersectionReferenceSystem, setPrevIntersectionReferenceSystem] = React.useState< IntersectionReferenceSystem | null | undefined >(null); - const [prevShowGrid, setPrevShowGrid] = React.useState(null); - const [prevContainerSize, setPrevContainerSize] = React.useState(null); - const [prevLayers, setPrevLayers] = React.useState(null); - const [prevBounds, setPrevBounds] = React.useState(null); + const [prevShowGrid, setPrevShowGrid] = React.useState(undefined); + const [prevContainerSize, setPrevContainerSize] = React.useState(undefined); + const [prevLayers, setPrevLayers] = React.useState(undefined); + const [prevBounds, setPrevBounds] = React.useState(undefined); const [currentViewport, setCurrentViewport] = React.useState(null); - const [prevViewport, setPrevViewport] = React.useState(null); - const [prevShowAxesLabels, setPrevShowAxesLabels] = React.useState(null); - const [prevShowAxes, setPrevShowAxes] = React.useState(null); - const [prevZFactor, setPrevZFactor] = React.useState(null); - const [prevHighlightItems, setPrevHighlightItems] = React.useState(null); + const [prevViewport, setPrevViewport] = React.useState(undefined); + const [prevShowAxesLabels, setPrevShowAxesLabels] = React.useState(undefined); + const [prevShowAxes, setPrevShowAxes] = React.useState(undefined); + const [prevZFactor, setPrevZFactor] = React.useState(undefined); + const [prevHighlightItems, setPrevHighlightItems] = React.useState(undefined); const [layerIds, setLayerIds] = React.useState([]); @@ -313,12 +313,12 @@ export function EsvIntersection(props: EsvIntersectionProps): React.ReactNode { } if ( - !(prevViewport && props.viewport) || - (!prevViewport && props.viewport) || - (prevViewport && !props.viewport) || - !fuzzyCompare(prevViewport[0], props.viewport[0], 0.0001) || - !fuzzyCompare(prevViewport[1], props.viewport[1], 0.0001) || - !fuzzyCompare(prevViewport[2], props.viewport[2], 0.0001) + !isEqual(prevViewport, props.viewport) || + (prevViewport && + props.viewport && + (!fuzzyCompare(prevViewport[0], props.viewport[0], 0.0001) || + !fuzzyCompare(prevViewport[1], props.viewport[1], 0.0001) || + !fuzzyCompare(prevViewport[2], props.viewport[2], 0.0001))) ) { if (props.viewport) { if ( @@ -461,14 +461,14 @@ export function EsvIntersection(props: EsvIntersectionProps): React.ReactNode { setInteractionHandler(null); setLayerIds([]); setPrevLayers([]); - setPrevAxesOptions(null); + setPrevAxesOptions(undefined); setPrevIntersectionReferenceSystem(null); - setPrevShowGrid(null); - setPrevContainerSize(null); - setPrevBounds(null); - setPrevViewport(null); - setPrevShowAxesLabels(null); - setPrevShowAxes(null); + setPrevShowGrid(undefined); + setPrevContainerSize(undefined); + setPrevBounds(undefined); + setPrevViewport(undefined); + setPrevShowAxesLabels(undefined); + setPrevShowAxes(undefined); newPixiRenderApplication.destroy(); newEsvController.removeAllLayers(); newEsvController.destroy(); @@ -483,7 +483,7 @@ export function EsvIntersection(props: EsvIntersectionProps): React.ReactNode {
); diff --git a/frontend/src/modules/_shared/components/EsvIntersection/layers/SurfaceStatisticalFanchartCanvasLayer.ts b/frontend/src/modules/_shared/components/EsvIntersection/layers/SurfaceStatisticalFanchartCanvasLayer.ts index a2c3243ec..2c13209be 100644 --- a/frontend/src/modules/_shared/components/EsvIntersection/layers/SurfaceStatisticalFanchartCanvasLayer.ts +++ b/frontend/src/modules/_shared/components/EsvIntersection/layers/SurfaceStatisticalFanchartCanvasLayer.ts @@ -102,7 +102,7 @@ export class SurfaceStatisticalFanchartsCanvasLayer { + return ( + + {additionalInformationItemToReadableString(key, value)} + + ); + }); +} + +export function ReadoutBox(props: ReadoutBoxProps): React.ReactNode { + if (props.readoutItems.length === 0) { + return null; + } + + return ( +
+ {props.readoutItems.map((item, index) => { + if (index < (props.maxNumItems ?? 3)) { + return ( +
+
+
+ {getLabelFromLayerData(item)} +
+ {makeAdditionalInformation(item)} +
+
+ ); + } + })} + {props.readoutItems.length > (props.maxNumItems ?? 3) && ( +
+ ...and {props.readoutItems.length - (props.maxNumItems ?? 3)} more +
+ )} +
+ ); +} diff --git a/frontend/src/modules/_shared/components/EsvIntersection/utilityComponents/Toolbar.tsx b/frontend/src/modules/_shared/components/EsvIntersection/utilityComponents/Toolbar.tsx new file mode 100644 index 000000000..e63f3d67b --- /dev/null +++ b/frontend/src/modules/_shared/components/EsvIntersection/utilityComponents/Toolbar.tsx @@ -0,0 +1,55 @@ +import { Button } from "@lib/components/Button"; +import { HoldPressedIntervalCallbackButton } from "@lib/components/HoldPressedIntervalCallbackButton/holdPressedIntervalCallbackButton"; +import { Add, FilterCenterFocus, Remove } from "@mui/icons-material"; + +export type ToolbarProps = { + visible: boolean; + zFactor: number; + onFitInView: () => void; + onVerticalScaleIncrease: () => void; + onVerticalScaleDecrease: () => void; +}; + +export function Toolbar(props: ToolbarProps): React.ReactNode { + function handleFitInViewClick() { + props.onFitInView(); + } + + function handleVerticalScaleIncrease() { + props.onVerticalScaleIncrease(); + } + + function handleVerticalScaleDecrease() { + props.onVerticalScaleDecrease(); + } + + if (!props.visible) { + return null; + } + + return ( +
+ + + + + + {props.zFactor.toFixed(2)} + + + +
+ ); +} + +function ToolBarDivider(): React.ReactNode { + return
; +} diff --git a/frontend/src/modules/_shared/components/EsvIntersection/utils/surfaceStatisticalFancharts.ts b/frontend/src/modules/_shared/components/EsvIntersection/utils/surfaceStatisticalFancharts.ts index ffbcf33d0..5e8b5ea0d 100644 --- a/frontend/src/modules/_shared/components/EsvIntersection/utils/surfaceStatisticalFancharts.ts +++ b/frontend/src/modules/_shared/components/EsvIntersection/utils/surfaceStatisticalFancharts.ts @@ -22,7 +22,7 @@ function mergeXAndYArrays(arrayX: number[], arrayY: number[]): number[][] { return arrayX.map((x, i) => [x, arrayY[i]]); } -export function makeSurfaceStatisticalFanchartFromRealizationSurfaces( +export function makeSurfaceStatisticalFanchartFromRealizationSurface( realizationSamplePoints: number[][], cumulatedLength: number[], surfaceName: string,