Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
rubenthoms committed Jun 18, 2024
1 parent 8440f18 commit 18f4a7c
Show file tree
Hide file tree
Showing 8 changed files with 629 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export function makeSurfaceStatisticalFanchartFromRealizationSurface(
realizationSamplePoints: number[][],
cumulatedLength: number[],
surfaceName: string,
stratColorMap: StratigraphyColorMap,
color: string,
visibility?: {
mean: boolean;
minMax: boolean;
Expand Down Expand Up @@ -56,8 +56,6 @@ export function makeSurfaceStatisticalFanchartFromRealizationSurface(
p90[i] = calcPercentile(values, 90);
}

const color = stratColorMap[surfaceName] || "black";

return {
color,
label: surfaceName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { BaseLayer } from "@modules/Intersection/utils/layers/BaseLayer";
import { GridLayer } from "@modules/Intersection/utils/layers/GridLayer";
import { SeismicLayer } from "@modules/Intersection/utils/layers/SeismicLayer";
import { SurfaceLayer } from "@modules/Intersection/utils/layers/SurfaceLayer";
import { SurfacesUncertaintyLayer } from "@modules/Intersection/utils/layers/SurfacesUncertaintyLayer";
import { WellpicksLayer } from "@modules/Intersection/utils/layers/WellpicksLayer";
import { QueryClient } from "@tanstack/query-core";

Expand All @@ -34,6 +35,8 @@ function makeLayer(type: LayerType, name: string, queryClient: QueryClient): Bas
return new SurfaceLayer(name, queryClient);
case LayerType.WELLPICKS:
return new WellpicksLayer(name, queryClient);
case LayerType.SURFACES_UNCERTAINTY:
return new SurfacesUncertaintyLayer(name, queryClient);
default:
throw new Error(`Layer type ${type} not supported`);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,305 @@
import React from "react";

import { SurfaceAttributeType_api, SurfaceMeta_api } from "@api";
import { apiService } from "@framework/ApiService";
import { EnsembleIdent } from "@framework/EnsembleIdent";
import { EnsembleSet } from "@framework/EnsembleSet";
import { WorkbenchSession, useEnsembleRealizationFilterFunc } from "@framework/WorkbenchSession";
import { WorkbenchSettings } from "@framework/WorkbenchSettings";
import { EnsembleDropdown } from "@framework/components/EnsembleDropdown";
import { defaultColorPalettes } from "@framework/utils/colorPalettes";
import { ColorPaletteSelector, ColorPaletteSelectorType } from "@lib/components/ColorPaletteSelector";
import { Dropdown, DropdownOption } from "@lib/components/Dropdown";
import { Input } from "@lib/components/Input";
import { PendingWrapper } from "@lib/components/PendingWrapper";
import { Select } from "@lib/components/Select";
import { ColorPalette } from "@lib/utils/ColorPalette";
import { ColorSet } from "@lib/utils/ColorSet";
import { useLayerSettings } from "@modules/Intersection/utils/layers/BaseLayer";
import {
SurfacesUncertaintyLayer,
SurfacesUncertaintyLayerSettings,
} from "@modules/Intersection/utils/layers/SurfacesUncertaintyLayer";
import { UseQueryResult, useQuery } from "@tanstack/react-query";

import { cloneDeep, isEqual } from "lodash";

import { fixupSetting } from "./utils";

export type SurfacesUncertaintyLayerSettingsComponentProps = {
layer: SurfacesUncertaintyLayer;
ensembleSet: EnsembleSet;
workbenchSession: WorkbenchSession;
workbenchSettings: WorkbenchSettings;
};

export function SurfacesUncertaintyLayerSettingsComponent(
props: SurfacesUncertaintyLayerSettingsComponentProps
): React.ReactNode {
const settings = useLayerSettings(props.layer);
const [newSettings, setNewSettings] = React.useState<SurfacesUncertaintyLayerSettings>(cloneDeep(settings));
const [prevSettings, setPrevSettings] = React.useState<SurfacesUncertaintyLayerSettings>(cloneDeep(settings));

if (!isEqual(settings, prevSettings)) {
setPrevSettings(settings);
setNewSettings(settings);
}

const ensembleFilterFunc = useEnsembleRealizationFilterFunc(props.workbenchSession);

const surfaceDirectoryQuery = useSurfaceDirectoryQuery(
newSettings.ensembleIdent?.getCaseUuid(),
newSettings.ensembleIdent?.getEnsembleName()
);

const fixupEnsembleIdent = fixupSetting(
"ensembleIdent",
props.ensembleSet.getEnsembleArr().map((el) => el.getIdent()),
newSettings
);
if (!isEqual(fixupEnsembleIdent, newSettings.ensembleIdent)) {
setNewSettings((prev) => ({ ...prev, ensembleIdent: fixupEnsembleIdent }));
}

if (fixupEnsembleIdent) {
const fixupRealizationNums = fixupRealizationNumsSetting(
newSettings.realizationNums,
ensembleFilterFunc(fixupEnsembleIdent)
);
if (!isEqual(fixupRealizationNums, newSettings.realizationNums)) {
setNewSettings((prev) => ({ ...prev, realizationNums: fixupRealizationNums }));
}
}

const availableAttributes: string[] = [];
const availableSurfaceNames: string[] = [];

if (surfaceDirectoryQuery.data) {
availableAttributes.push(
...Array.from(
new Set(
surfaceDirectoryQuery.data
.filter((el) => el.attribute_type === SurfaceAttributeType_api.DEPTH)
.map((el) => el.attribute_name)
)
)
);

const fixupAttribute = fixupSetting("attribute", availableAttributes, newSettings);
if (!isEqual(fixupAttribute, newSettings.attribute)) {
setNewSettings((prev) => ({ ...prev, attribute: fixupAttribute }));
}
}

if (surfaceDirectoryQuery.data && newSettings.attribute) {
availableSurfaceNames.push(
...Array.from(
new Set(
surfaceDirectoryQuery.data
.filter((el) => el.attribute_name === newSettings.attribute)
.map((el) => el.name)
)
)
);

const fixupSurfaceNames = fixupSurfaceNamesSetting(newSettings.surfaceNames, availableSurfaceNames);
if (!isEqual(fixupSurfaceNames, newSettings.surfaceNames)) {
setNewSettings((prev) => ({ ...prev, surfaceNames: fixupSurfaceNames }));
}

props.layer.maybeRefetchData();
}

React.useEffect(
function propagateSettingsChange() {
props.layer.maybeUpdateSettings(cloneDeep(newSettings));
},
[newSettings, props.layer]
);

React.useEffect(
function maybeRefetchData() {
props.layer.setIsSuspended(surfaceDirectoryQuery.isFetching);
if (!surfaceDirectoryQuery.isFetching) {
props.layer.maybeRefetchData();
}
},
[surfaceDirectoryQuery.isFetching, props.layer, newSettings]
);

function handleEnsembleChange(ensembleIdent: EnsembleIdent | null) {
setNewSettings((prev) => ({ ...prev, ensembleIdent }));
}

function handleRealizationsChange(realizationNums: string[]) {
setNewSettings((prev) => ({ ...prev, realizationNums: realizationNums.map((el) => parseInt(el)) }));
}

function handleAttributeChange(attribute: string) {
setNewSettings((prev) => ({ ...prev, attribute }));
}

function handleSurfaceNamesChange(surfaceNames: string[]) {
setNewSettings((prev) => ({ ...prev, surfaceNames }));
}

function handleResolutionChange(e: React.ChangeEvent<HTMLInputElement>) {
setNewSettings((prev) => ({ ...prev, resolution: parseFloat(e.target.value) }));
}

function handleColorPaletteChange(colorPalette: ColorPalette) {
props.layer.setColorSet(new ColorSet(colorPalette));
}

const availableRealizations: number[] = [];
if (fixupEnsembleIdent) {
availableRealizations.push(...ensembleFilterFunc(fixupEnsembleIdent));
}

return (
<div className="table text-sm border-spacing-y-2 border-spacing-x-3 w-full">
<div className="table-row">
<div className="table-cell w-24 align-middle">Ensemble</div>
<div className="table-cell">
<EnsembleDropdown
value={props.layer.getSettings().ensembleIdent}
ensembleSet={props.ensembleSet}
onChange={handleEnsembleChange}
debounceTimeMs={600}
/>
</div>
</div>
<div className="table-row">
<div className="table-cell align-middle">Realizations</div>
<div className="table-cell">
<Select
options={makeRealizationOptions(availableRealizations)}
value={newSettings.realizationNums.map((el) => el.toString())}
onChange={handleRealizationsChange}
debounceTimeMs={600}
size={5}
multiple
/>
</div>
</div>
<div className="table-row">
<div className="table-cell align-middle">Attribute</div>
<div className="table-cell">
<PendingWrapper
isPending={surfaceDirectoryQuery.isFetching}
errorMessage={surfaceDirectoryQuery.error?.message}
>
<Dropdown
options={makeAttributeOptions(availableAttributes)}
value={newSettings.attribute ?? undefined}
onChange={handleAttributeChange}
showArrows
debounceTimeMs={600}
/>
</PendingWrapper>
</div>
</div>
<div className="table-row">
<div className="table-cell align-top">Surface names</div>
<div className="table-cell max-w-0">
<PendingWrapper
isPending={surfaceDirectoryQuery.isFetching}
errorMessage={surfaceDirectoryQuery.error?.message}
>
<Select
options={makeSurfaceNameOptions(availableSurfaceNames)}
value={newSettings.surfaceNames ?? undefined}
onChange={handleSurfaceNamesChange}
size={5}
multiple
debounceTimeMs={600}
/>
</PendingWrapper>
</div>
</div>
<div className="table-row">
<div className="table-cell align-middle">Sample resolution</div>
<div className="table-cell">
<Input
value={newSettings.resolution}
onChange={handleResolutionChange}
debounceTimeMs={600}
endAdornment="m"
type="number"
min={1}
/>
</div>
</div>
<div className="table-row">
<div className="table-cell align-middle">Color set</div>
<div className="table-cell">
<ColorPaletteSelector
type={ColorPaletteSelectorType.Categorical}
selectedColorPaletteId={props.layer.getColorSet().getColorPalette().getId()}
colorPalettes={defaultColorPalettes}
onChange={handleColorPaletteChange}
/>
</div>
</div>
</div>
);
}

function makeRealizationOptions(realizations: readonly number[]): DropdownOption[] {
return realizations.map((realization) => ({ label: realization.toString(), value: realization.toString() }));
}

function makeAttributeOptions(attributes: string[]): DropdownOption[] {
return attributes.map((attr) => ({ label: attr, value: attr }));
}

function makeSurfaceNameOptions(surfaceNames: string[]): DropdownOption[] {
return surfaceNames.map((surfaceName) => ({ label: surfaceName, value: surfaceName }));
}

const STALE_TIME = 60 * 1000;
const CACHE_TIME = 60 * 1000;

export function useSurfaceDirectoryQuery(
caseUuid: string | undefined,
ensembleName: string | undefined
): UseQueryResult<SurfaceMeta_api[]> {
return useQuery({
queryKey: ["getSurfaceDirectory", caseUuid, ensembleName],
queryFn: () => apiService.surface.getSurfaceDirectory(caseUuid ?? "", ensembleName ?? ""),
staleTime: STALE_TIME,
gcTime: CACHE_TIME,
enabled: Boolean(caseUuid && ensembleName),
});
}

function fixupRealizationNumsSetting(
currentRealizationNums: readonly number[],
validRealizationNums: readonly number[]
): number[] {
if (validRealizationNums.length === 0) {
return [...currentRealizationNums];
}

let adjustedRealizationNums = currentRealizationNums.filter((el) => validRealizationNums.includes(el));

if (adjustedRealizationNums.length === 0) {
adjustedRealizationNums = [...validRealizationNums];
}

return adjustedRealizationNums;
}

function fixupSurfaceNamesSetting(currentSurfaceNames: string[], validSurfaceNames: string[]): string[] {
if (validSurfaceNames.length === 0) {
return currentSurfaceNames;
}

let adjustedSurfaceNames = currentSurfaceNames.filter((el) => validSurfaceNames.includes(el));

if (adjustedSurfaceNames.length === 0) {
adjustedSurfaceNames = [validSurfaceNames[0]];
}

return adjustedSurfaceNames;
}
12 changes: 12 additions & 0 deletions frontend/src/modules/Intersection/settings/components/layers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
import { isGridLayer } from "@modules/Intersection/utils/layers/GridLayer";
import { isSeismicLayer } from "@modules/Intersection/utils/layers/SeismicLayer";
import { isSurfaceLayer } from "@modules/Intersection/utils/layers/SurfaceLayer";
import { isSurfacesUncertaintyLayer } from "@modules/Intersection/utils/layers/SurfacesUncertaintyLayer";
import { isWellpicksLayer } from "@modules/Intersection/utils/layers/WellpicksLayer";
import { Dropdown, MenuButton } from "@mui/base";
import {
Expand All @@ -48,6 +49,7 @@ import { isEqual } from "lodash";
import { GridLayerSettingsComponent } from "./layerSettings/gridLayer";
import { SeismicLayerSettingsComponent } from "./layerSettings/seismicLayer";
import { SurfaceLayerSettingsComponent } from "./layerSettings/surfaceLayer";
import { SurfacesUncertaintyLayerSettingsComponent } from "./layerSettings/surfacesUncertaintyLayer";
import { WellpicksLayerSettingsComponent } from "./layerSettings/wellpicksLayer";

import { layersAtom } from "../atoms/layersAtoms";
Expand Down Expand Up @@ -447,6 +449,16 @@ function LayerItem(props: LayerItemProps): React.ReactNode {
/>
);
}
if (isSurfacesUncertaintyLayer(layer)) {
return (
<SurfacesUncertaintyLayerSettingsComponent
ensembleSet={props.ensembleSet}
workbenchSession={props.workbenchSession}
workbenchSettings={props.workbenchSettings}
layer={layer}
/>
);
}
return null;
}

Expand Down
2 changes: 2 additions & 0 deletions frontend/src/modules/Intersection/typesAndEnums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ export enum LayerType {
SEISMIC = "seismic",
SURFACES = "surfaces",
WELLPICKS = "wellpicks",
SURFACES_UNCERTAINTY = "surfaces-uncertainty",
}

export const LAYER_TYPE_TO_STRING_MAPPING = {
[LayerType.GRID]: "Grid",
[LayerType.SEISMIC]: "Seismic",
[LayerType.SURFACES]: "Surfaces",
[LayerType.WELLPICKS]: "Wellpicks",
[LayerType.SURFACES_UNCERTAINTY]: "Surfaces Uncertainty",
};

export enum LayerActionType {
Expand Down
Loading

0 comments on commit 18f4a7c

Please sign in to comment.